How to load data in sqflite database? - flutter

Database database = await openDatabase(
dbPathWithDatabase,
version: 1,
onCreate: (Database database, int version) async {
print("\n\nCalling database on create\n\n");
ByteData data = await rootBundle.load(join("assets", "database.db"));
List<int> bytes =
data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
print(bytes);
await new File(dbPathWithDatabase).writeAsBytes(bytes);
},
);
The above code is not copying the data to my database. But the database is created successfully.
database.db already exist in assets folder and i want to copy all the data from it.
Here i am calling it from openDatabase which cause the problem.
I can move the code above openDatabase. But i want to know the correct way to populate the data

Related

Flutter Desktop App (MacOS) - Unable to find SQLITE DB and creates empty db in MacOS /Users/<user>/Documents directory

There's some very odd problems when using flutter for MacOs apps.
I'm using the following Database initialisation:
_initDatabase() async {
Directory dbDirectory = await getApplicationDocumentsDirectory();
String path = join(dbDirectory.path, _databaseName);
//ByteData data = await rootBundle.load("assets/mydatabase.sqlite");
//List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
//await File(path).writeAsBytes(bytes);
return await openDatabase(path,
version: _databaseVersion,
//onCreate: _onCreate
);}
What is the issue?
I keep on getting error that it cannot find the table. The table absolutely exists.
But then I checked the logs and it appears it is using the /Users/myname/Documents to store the database file.
that's not what I want. The database file for this application is in the assets folder, and it should remain (when it is packaged) within the app bundle.
How do I specify the assets folder??
What am I doing wrong??
The create function, by the way, isn't working either.
Future _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE $table (
$columnId INTEGER PRIMARY KEY,
$col1 TEXT,
$col2 TEXT,
$col3 TEXT
)
''');}
Im exhausted from trying...

Where is my sqflite database stored inside Flutter app?

I'm creating an encrypted sqflite database with sqflite_sqlcipher and would like to open it with the DB Browser for SQLCipher. For that i only need to find my .db file but I can't seem to figure out where this file is stored inside my app.
I have printed the path to the db in my init function and the output is below:
Future<Database> _initDatabase() async {
print("Database path: ${await getDatabasesPath()}");
final dbPath = await getDatabasesPath();
final path = join(dbPath, "documents.db");
final pw = await getPassword();
return await openDatabase(
path,
password: pw,
version: 1,
onCreate: _onCreate,
);
}
Database path: /data/user/0/com.example.did/databases
You could access it using Android Studio, look for Device File Manager and from there, you can access the file path.

Sqlite onCreate isn't invoked after dropping/deleting the table from the database/storage

Minimal reproducible code:
class Helper {
Database _db;
Future<Database> initDb() async {
if (_db == null) {
final directory = await getApplicationDocumentsDirectory();
_db = await openDatabase(join(directory.path, 'foo.db'), version: 1, onCreate: _onCreate);
}
return _db;
}
Future<void> _onCreate(Database db, _) async {
print('onCreate');
await db.transaction((txn) async {
await txn.execute('CREATE TABLE tableName(abc TEXT)');
});
}
}
Here's my main method:
void main() async {
final directory = await getApplicationDocumentsDirectory();
final file = File(join(directory.path, 'foo.db'));
if (await file.exists()) {
await file.delete();
}
// If there had been a file, it's deleted now.
final helper = Helper();
await helper.initDb(); // This must fire `onCreate` but it doesn't.
}
Every time you run the main method, it should execute the onCreate method in Helper class but it only does that once. What am I doing wrong here?
The issue description has changed since the beginning and it is not easy to make a proper explanation in a comment so here is yet another answer.
All existing responses remain valid but the issue now is moving to something like onCreate not called after deleting the database.
Every time you run the main method, it should execute the onCreate method in Helper class but it only does that once. What am I doing wrong here?
You don't really specify how you run (i.e. do you stop the application before), so I'm assuming you are just pressing the run button on your IDE which performs a hot-restart.
While you might be enclined to simply delete the file, you should however use
deleteDatabase to properly delete a database.
// Do not call File.delete, it will not work in a hot restart scenario
await File(path).delete();
// Instead do
await deleteDatabase(path);
it will properly close any existing database connection
it will properly handle the hot-restart scenario which put SQLite in a
weird state (basically the 'dart' side think the database is closed while
the database is in fact open on the native side)
If you call File.delete, while you might think it work (i.e. the file does not
exist anymore), since the database might still be opened in a hot restart scenario
the next open will re-use the open connection and at some point will get written
with the old data and onCreate will not be called the next time you open
the database.
onCreate will be executed only when there is no database file. Not when there is no table in the db. If you delete database file then there will be a print in console with message onCreate. Here is an example:
void main() async {
final directory = await getApplicationDocumentsDirectory();
final path = join(directory.path, 'foo.db');
await File(path).delete();
final helper = Helper();
await helper.initDb();
// await helper.dropTable();
}
This code prints a log message every run of a program.
This is not how Sqflite works.
The very first time when you create a database you give it a version number. In your case the version is 1.
Sqflite will check if database exists on the devices if it exists it checks the version number of db on device and version number of your code, if version is not the same it will call onUpgrade if new version is greater than old db version. and on downgrade if old version is greater than new version. OnCreate won't be called. onCreate is only called the very first time user install your app. Even if you drop your tables afterwards. If you need to update your database in future on a production app, you have to write onUpgrade method, in which you have to explicitly drop your tables and call onCreate yourself. and upgrade the database version.
See the following code. Use this code setup and change version to a higher version number whenever you need to dropAll tables and createAgain.
If you don't want to use the versions and explicitly drop tables and recreate them. You have to call _onCreate yourself after dropping a table.
class Helper {
Database _db;
Future<Database> initDb() async {
if (_db == null) {
final directory = await getDatabasesPath();
final path = join(directory, 'foo.db');
_db = await openDatabase(
path,
version: 2, //+1 to this number whenever you want to update
onCreate: _onCreate,
onUpgrade: _onUpgrade,
);
}
return _db;
}
Future<void> _onUpgrade(
Database db, int previousVersion, int newVersion) async {
_db = db;
await dropTable();
await _onCreate(db, newVersion);
}
Future<void> _onCreate(Database db, int version) async {
print('onCreate');
await db.transaction((txn) async {
await txn.execute('CREATE TABLE tableName(abc TEXT)');
});
}
Future<void> dropTable() async {
await _db.transaction((txn) async {
await txn.execute('DROP TABLE IF EXISTS tableName');
});
}
It is the default behavior because onCreate Method is executed when you create the database. Since the database is created before it is not executed again.
If you intent to clean the rows of the table then use the TRUNCATE command. For more information check the link comparing drop table and truncate table here

How to check and delete all existing db of sqflite in flutter?

As I am new to flutter, I have created new DB whenever I have faced an issue, But now I need to remove other unused DB's. How can I check existing DB and delete them except currently using?
//final demoDb = 'demoDb1.db';
//final demoDb = 'demoDb2.db';
//final demoDb = 'demoDb3.db';
//final demoDb = 'demoDb4.db';
final demoDb = 'demoDb5.db'; //Currently using this one
createDatabase() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, demoDb);
var database = await openDatabase(path,
version: 1, onCreate: initDB, onUpgrade: onUpgrade);
return database;
}
Thanks in advance.
There is no flutter to way to list existing databases, you can however list files (a sqflite database is a file) in the directory you created them (I suggest using getDatabasesPath instead of getApplicationDocumentsDirectory).
You can then check if database exists (databaseExists) and delete it (deleteDatabase) assuming it is not open (in this case you must close it first)

Transaction/batch in a newly created SQFlite database

The following only happens with the database just created as in the code. A previously existing database works all right.
I have the usual singleton setup for the database helper, the relevant part being:
Future<Database> get database async {
// ...
db ??= await openDatabase(
path.join(await getDatabasesPath(), 'database.db'),
onCreate: (db, version) async {
final batch = db.batch();
batch.execute('CREATE TABLE table1 ...');
batch.execute('CREATE TABLE table2 ...');
await batch.commit(noResult: true);
},
// ...
return db;
}
Let's suppose the database doesn't exist yet. I call the following routine:
final db = await database;
await db.transaction((txn) async {
await txn.delete('table1');
final batch = txn.batch();
for (data in newData1)
batch.insert('table1', data.toJson()));
await batch.commit(noResult: true);
await txn.delete('table2');
final batch = txn.batch();
for (data in newData2)
batch.insert('table2', data.toJson()));
await batch.commit(noResult: true);
});
The transaction and batch calls execute without error. When the whole operation is actually executed at the end, it stops on the first DELETE FROM table1 SQL operation with a DatabaseException(attempt to write a readonly database(Sqlite code 1032) (running on Android).
I checked that the singleton is a singleton, openDatabase is not called twice. I also tried the transaction with exclusive: false, no difference.
The likely cause of the issue is that the table can't be accessed. A known workaround for this case is to open the database and close it before deleting, as mentioned in this GitHub issue thread.