How to add new table to sqlite? - dart

I need to add a new column name id INTEGER AUTOINCREMENT and a new table for the current database for my existing table. How to use 'onUpgrade'? Is it need to change the version number?
initDb() async {
io.Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, "HelperDatabase.db");
var theDb = await openDatabase(path, version: 1, onCreate: _onCreate, onUpgrade: _onUpgrade);
return theDb;
}
How to use _onUpgrade
void _onUpgrade(Database db, int oldVersion, int newVersion)async{
}
Is it need to add column also?
void _onCreate(Database db, int version) async {
await db.execute(
"""CREATE TABLE AssetAssemblyTable(e INTEGER, a INTEGER, c INTEGER)""");

To update your DB from old version, you should change version to 2.
You should change onCreate and onUpdate like below.
// This is called for new users who have no old db
void _onCreate(Database db, int version) async {
// if `AssetAssemblyTable` has a new column in version 2, add the column here.
await db.execute(
"""CREATE TABLE AssetAssemblyTable(e INTEGER, a INTEGER, c INTEGER)""");
)
await db.execute("CREATE TABLE NewTable...") // create new Table
}
// This is called for existing users who have old db(version 1)
void _onUpgrade(Database db, int oldVersion, int newVersion)async{
// In this case, oldVersion is 1, newVersion is 2
if (oldVersion == 1) {
await db.execute("ALTER TABLE AssetAssemblyTable...") // add new column to existing table.
await db.execute("CREATE TABLE NewTable...") // create new Table
}
}
more example is below
https://github.com/tekartik/sqflite/blob/master/sqflite/doc/migration_example.md

Here what I've done :
class SqliteDB {
static final SqliteDB _instance = new SqliteDB.internal();
factory SqliteDB() => _instance;
static Database? _db;
Future<Database?> get db async {
if (_db != null) {
return _db;
}
_db = await initDb();
return _db;
}
SqliteDB.internal();
/// Initialize DB
initDb() async {
io.Directory documentDirectory = await
getApplicationDocumentsDirectory();
String path = join(documentDirectory.path, "RadiosStations.db");
var taskDb = await openDatabase(
//open the database or create a database if there isn't any path,
version: 2, onCreate: (Database db, int version) async {
await db.execute(
"""CREATE TABLE AssetAssemblyTable(e INTEGER, a INTEGER, c
INTEGER)""");
)
await db.execute("CREATE TABLE NewTable...") // create new Table;
},
onUpgrade: (Database db, int oldVersion, int newVersion)async{
// In this case, oldVersion is 1, newVersion is 2
if (oldVersion == 1) {
await db.execute(
"""CREATE TABLE AssetAssemblyTable(e INTEGER, a INTEGER, c
INTEGER)""");
)
await db.execute("CREATE TABLE NewTable...") // create new Table;
}}
);
return taskDb;}

Related

Flutter iOS App sqflite database data lost after app upgrade

We have an app build entirely by flutter. When we send app upgrades to google store the sqflite database data is not lost when updating apk in Android device.
But in iOS Device after user updates app to new version all database data lost.
flutter v1.17.5
Package used for data persistence:
sqflite: ^1.3.1
path_provider: ^1.6.11
Can you please help to solve this problem. Data of user must not be deleted after app update to new version. Maybe I need to use other type of data persistence or how can I solve this.
Here are parts where I initialize Database in DatabaseHelper class:
class DatabaseHelper {
static DatabaseHelper _databaseHelper; // Singletone DatabaseHelper
static Database _database; // Singletone Database
String wifiSystemTable = 'wifi_system_table';
String colId = 'id';
String colDataType = 'data_type';
String colLocationName = 'location_name';
String colBackground = 'background';
String colLocationId = 'location_id';
String colDeviceType = 'device_type';
String colIp = 'ip';
String colName1 = 'name1';
String colName2 = 'name2';
String colName3 = 'name3';
String colRemotePort = 'remote_port';
DatabaseHelper._createInstance(); // Named constractor to create instance of Database Helper
factory DatabaseHelper() {
if (_databaseHelper == null) {
_databaseHelper = DatabaseHelper
._createInstance(); //This is execute only once, singletone object
}
return _databaseHelper;
}
Future<Database> get database async {
if (_database == null) {
_database = await initializeDatabase();
}
return _database;
}
Future<Database> initializeDatabase() async {
//Get the directory path for both Android and IOS to store Database
Directory directory = await getApplicationDocumentsDirectory();
String path = p.join(directory.toString(), 'wifi.db');
//Open/create the database at the given path
var wifiSystemDatabase =
await openDatabase(path, version: 1, onCreate: _createDb);
return wifiSystemDatabase;
}
void _createDb(Database db, int newVersion) async {
await db.execute(
'CREATE TABLE $wifiSystemTable($colId INTEGER PRIMARY KEY, $colDataType INTEGER, $colLocationName TEXT, $colBackground TEXT, $colLocationId INTEGER, $colDeviceType INTEGER, $colIp TEXT, $colName1 TEXT, $colName2 TEXT, $colName3 TEXT, $colRemotePort TEXT)');
}
Thanks very much
I think your DB path is getting changed. Try this.
Future<Database> initializeDatabase() async {
//Get the directory path for both Android and IOS to store Database
String databasesPath = await getDatabasesPath();
String path = p.join(databasesPath, 'wifi.db');
//Open/create the database at the given path
var wifiSystemDatabase = await openDatabase(path, version: 1, onCreate: _createDb);
return wifiSystemDatabase;
}
Hope it helps :)

How to get the number of rows in a database table with Flutter SQFlite

How do I get the row count of a database table in Flutter. I am using the SQFlite plugin.
I assume that it is similar to Android, but Android has DatabaseUtils.queryNumEntries(db, TABLE_NAME). Does SQFlite have something similar?
I am answering the question to the best of my knowledge below, but I would be glad for a better answer if there is one.
You can use
int count = Sqflite.firstIntValue(await db.rawQuery('SELECT COUNT(*) FROM table_name'));
where db is an SQFlite Database.
Source: I found this here and in the source code.
Try this function:
Future<int> getCount() async {
//database connection
Database db = await this.database;
var x = await db.rawQuery('SELECT COUNT (*) from
$Table');
int count = Sqflite.firstIntValue(x);
return count;
}
No more hard function invocations needed. just try with .length
If you are using a data model you can use its' name at MODEL_NAME.
Future<int> getCount() async {
Database db = await this.database;
var result = await db.query(MODEL_NAME.TABLE_NAME);
int count = result.length;
return count;
}
I believe a better way to do this is inside the DatabaseHelper/Service class:
class DbService {
static Database _db; //static = only a single copy of _db is shared among all the instances of this class (Database)
static int _count;
static int get count => _count;
...
this way every time a query gets executed the count updates, this also makes it possible to update the count on queries with filters without having to write a new function for it 😎:
List<Map> result = await db.query('myDatabase');
_count = result.length;
return result;
}
and then simply use DbService.count to get the count
You can use the magic column COUNT(*) as shown in the docs
Hence, the count function would look something like:
#override
Future<int> countUsersWithFirstName(String firstName) async {
return Sqflite.firstIntValue(await db.query(
'users',
columns: ['COUNT(*)'],
where: 'firstName = ?',
whereArgs: [firstName],
)) ??
0;
}
You can also use
List<Map> list = await db.rawQuery('SELECT * FROM table_name');
int count = list.length;
Try this code:
class DatabaseHelper() {
Future<List<Map<String, dynamic>>> getDataInMap() async {
Database database = await this.database;
return database.query("ahkam_fiqhia");
}
Future<List<BooksModel>> getModelsFromMapList() async {
List<Map<String, dynamic>> mapList = await getDataInMap();
List<DataModel> dataModel = List();
for (int i = 0; i < mapList.length; i++) {
dataModel(DataModel(mapList[i]));
}
print(mapList.length);
return dataModel;
}
}
Add initState() function in your view then call getModelsFromMapList function:
class _MainView extends State<MainView> {
DatabaseHelper dbHelper = databaseHelper();
#override
void initState() {
super.initState();
databaseHelper.getModelsFromMapList();
}
}
This is my code using for custom column condition count
Future<int?> getCustomTaskCount(String t1) async {
Database? _database = await database;
if (_database != null) {
var result = Sqflite.firstIntValue(await _database.rawQuery(
'SELECT COUNT (*) FROM tableName WHERE columnName=?',
[t1]));
print(result);
return result;
}}

How to do a database insert with SQFlite in Flutter

How do you insert data into a database in Flutter using the SQFlite plugin?
There are a number of problem solving questions out there but none that I could find to add a canonical answer to. My answer is below.
Add the dependencies
Open pubspec.yaml and in the dependency section add the following lines:
sqflite: ^1.0.0
path_provider: ^0.4.1
The sqflite is the SQFlite plugin of course and the path_provider will help us get the user directory on Android and iPhone.
Make a database helper class
I'm keeping a global reference to the database in a singleton class. This will prevent concurrency issues and data leaks (that's what I hear, but tell me if I'm wrong). You can also add helper methods (like insert) in here for accessing the database.
Create a new file called database_helper.dart and paste in the following code:
import 'dart:io' show Directory;
import 'package:path/path.dart' show join;
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart' show getApplicationDocumentsDirectory;
class DatabaseHelper {
static final _databaseName = "MyDatabase.db";
static final _databaseVersion = 1;
static final table = 'my_table';
static final columnId = '_id';
static final columnName = 'name';
static final columnAge = 'age';
// make this a singleton class
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
// only have a single app-wide reference to the database
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 {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, _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,
$columnName TEXT NOT NULL,
$columnAge INTEGER NOT NULL
)
''');
}
}
Insert data
We'll use an async method to do our insert:
_insert() async {
// get a reference to the database
// because this is an expensive operation we use async and await
Database db = await DatabaseHelper.instance.database;
// row to insert
Map<String, dynamic> row = {
DatabaseHelper.columnName : 'Bob',
DatabaseHelper.columnAge : 23
};
// do the insert and get the id of the inserted row
int id = await db.insert(DatabaseHelper.table, row);
// show the results: print all rows in the db
print(await db.query(DatabaseHelper.table));
}
Notes
You will have to import the DatabaseHelper class and sqflite if you are in another file (like main.dart).
The SQFlite plugin uses a Map<String, dynamic> to map the column names to the data in each row.
We didn't specify the id. SQLite auto increments it for us.
Raw insert
SQFlite also supports doing a raw insert. This means that you can use a SQL string. Lets insert the same row again using rawInsert().
db.rawInsert('INSERT INTO my_table(name, age) VALUES("Bob", 23)');
Of course, we wouldn't want to hard code those values into the SQL string, but we also wouldn't want to use interpelation like this:
String name = 'Bob';
int age = 23;
db.rawInsert('INSERT INTO my_table(name, age) VALUES($name, $age)'); // Dangerous!
That would open us up to SQL injection attacks. Instead we can use data binding like this:
db.rawInsert('INSERT INTO my_table(name, age) VALUES(?, ?)', [name, age]);
The [name, age] are filled in for the question mark placeholders in (?, ?). The table and column names are safer to use interpelation for, so we could do this finally:
String name = 'Bob';
int age = 23;
db.rawInsert(
'INSERT INTO ${DatabaseHelper.table}'
'(${DatabaseHelper.columnName}, ${DatabaseHelper.columnAge}) '
'VALUES(?, ?)', [name, age]);
Supplemental code
For your copy-and-paste convenience, here is the layout code for main.dart:
import 'package:flutter/material.dart';
import 'package:flutter_db_operations/database_helper.dart';
import 'package:sqflite/sqflite.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'SQFlite Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('sqflite'),
),
body: RaisedButton(
child: Text('insert', style: TextStyle(fontSize: 20),),
onPressed: () {_insert();},
),
);
}
_insert() async {
// get a reference to the database
// because this is an expensive operation we use async and await
Database db = await DatabaseHelper.instance.database;
// row to insert
Map<String, dynamic> row = {
DatabaseHelper.columnName : 'Bob',
DatabaseHelper.columnAge : 23
};
// do the insert and get the id of the inserted row
int id = await db.insert(DatabaseHelper.table, row);
// raw insert
//
// String name = 'Bob';
// int age = 23;
// int id = await db.rawInsert(
// 'INSERT INTO ${DatabaseHelper.table}'
// '(${DatabaseHelper.columnName}, ${DatabaseHelper.columnAge}) '
// 'VALUES(?, ?)', [name, age]);
print(await db.query(DatabaseHelper.table));
}
}
Going on
This post is a development from my previous post: Simple SQFlite database example in Flutter. See that post for other SQL operations and advice.
get in pubspec.yaml
add
dependency:
sqflite: any,
path_provider: any,
intl: ^0.15.7
then click package get and package upgrade

Google Dataflow write multiple line in BigQuery

I have a simple flow which aim is to write two lines in one BigQuery Table.
I use a DynamicDestinations because after that I will write on mutliple Table, on that example it's the same table...
The problem is that I only have 1 line in my BigQuery table at the end.
It stacktrace I see the following error on the second insert
"
status: {
code: 6
message: "Already Exists: Job sampleprojet3:b9912b9b05794aec8f4292b2ae493612_eeb0082ade6f4a58a14753d1cc92ddbc_00001-0"
}
"
What does it means ?
Is it related to this limitation ?
https://github.com/GoogleCloudPlatform/DataflowJavaSDK/issues/550
How can I do the job ?
I use BeamSDK 2.0.0, I have try with 2.1.0 (same result)
The way I launch :
mvn compile exec:java -Dexec.mainClass=fr.gireve.dataflow.LogsFlowBug -Dexec.args="--runner=DataflowRunner --inputDir=gs://sampleprojet3.appspot.com/ --project=sampleprojet3 --stagingLocation=gs://dataflow-sampleprojet3/tmp" -Pdataflow-runner
Pipeline p = Pipeline.create(options);
final List<String> tableNameTableValue = Arrays.asList("table1:value1", "table1:value2", "table2:value1", "table2:value2");
p.apply(Create.of(tableNameTableValue)).setCoder(StringUtf8Coder.of())
.apply(BigQueryIO.<String>write()
.withCreateDisposition(BigQueryIO.Write.CreateDisposition.CREATE_IF_NEEDED)
.withWriteDisposition(BigQueryIO.Write.WriteDisposition.WRITE_APPEND)
.to(new DynamicDestinations<String, KV<String, String>>() {
#Override
public KV<String, String> getDestination(ValueInSingleWindow<String> element) {
final String[] split = element.getValue().split(":");
return KV.of(split[0], split[1]) ;
}
#Override
public Coder<KV<String, String>> getDestinationCoder() {
return KvCoder.of(StringUtf8Coder.of(), StringUtf8Coder.of());
}
#Override
public TableDestination getTable(KV<String, String> row) {
String tableName = row.getKey();
String tableSpec = "sampleprojet3:testLoadJSON." + tableName;
return new TableDestination(tableSpec, "Table " + tableName);
}
#Override
public TableSchema getSchema(KV<String, String> row) {
List<TableFieldSchema> fields = new ArrayList<>();
fields.add(new TableFieldSchema().setName("myColumn").setType("STRING"));
TableSchema ts = new TableSchema();
ts.setFields(fields);
return ts;
}
})
.withFormatFunction(new SerializableFunction<String, TableRow>() {
public TableRow apply(String row) {
TableRow tr = new TableRow();
tr.set("myColumn", row);
return tr;
}
}));
p.run().waitUntilFinish();
Thanks
DynamicDestinations associates each element with a destination - i.e. where the element should go. Elements are routed to BigQuery tables according to their destinations: 1 destination = 1 BigQuery table with a schema: the destination should include just enough information to produce a TableDestination and a schema. Elements with the same destination go to the same table, elements with different destinations go to different tables.
Your code snippet uses DynamicDestinations with a destination type that contains both the element and the table, which is unnecessary, and of course, violates the constraint above: elements with a different destination end up going to the same table: e.g. KV("table1", "value1") and KV("table1", "value2") are different destinations but your getTable maps them to the same table table1.
You need to remove the element from your destination type. That will also lead to simpler code. As a side note, I think you don't need to override getDestinationCoder() - it can be inferred automatically.
Try this:
.to(new DynamicDestinations<String, String>() {
#Override
public String getDestination(ValueInSingleWindow<String> element) {
return element.getValue().split(":")[0];
}
#Override
public TableDestination getTable(String tableName) {
return new TableDestination(
"sampleprojet3:testLoadJSON." + tableName, "Table " + tableName);
}
#Override
public TableSchema getSchema(String tableName) {
List<TableFieldSchema> fields = Arrays.asList(
new TableFieldSchema().setName("myColumn").setType("STRING"));
return new TableSchema().setFields(fields);
}
})

'System.Linq.IQueryable<NewsSite.Models.Domain.Tbl_News>' does not contain a definition

I Create A News Site With MVC5 But I Have Problem .
in Model i Create A Repository Folder And in this i Create Rep_Setting for
Connect to Tbl_Setting in DataBase .
public class Rep_Setting
{
DataBase db = new DataBase();
public Tbl_Setting Tools()
{
try
{
var qGetSetting = (from a in db.Tbl_Setting
select a).FirstOrDefault();
return qGetSetting;
}
catch (Exception)
{
return null;
}
}
}
And i Create a Rep_News for Main Page .
DataBase db = new DataBase();
Rep_Setting RSetting = new Rep_Setting();
public List<Tbl_News> GetNews()
{
try
{
List<Tbl_News> qGetNews = (from a in db.Tbl_News
where a.Type.Equals("News")
select a).OrderByDescending(s => s.ID).Skip(0).Take(RSetting.Tools().CountNewsInPage).ToList();
return qGetNews;
}
catch (Exception ex)
{
return null;
}
}
But This Code Have Error to Me
OrderByDescending(s=>s.ID).Skip(0).Take(RSetting.Tools().CountNewsInPage).ToList();
Error :
Error 18 'System.Linq.IQueryable<NewsSite.Models.Domain.Tbl_News>' does
not contain a definition for 'Take' and the best extension method overload
'System.Linq.Queryable.Take<TSource>(System.Linq.IQueryable<TSource>, int)' has
some invalid arguments
E:\MyProject\NewsSite\NewsSite\Models\Repository\Rep_News.cs 50 52 NewsSite
How i Resolve it ?
Try it this way. The plan of debugging is to split your execution, this also makes for a more reusable method in many cases. And a good idea is to avoid using null and nullables if you can, if you use them "on purpose" the you must have a plan for them.
DataBase db = new DataBase();
Rep_Setting RSetting = new Rep_Setting();
public List<Tbl_News> GetNews()
{
int skip = 0;
Tbl_Setting tools = RSetting.Tools();
if(tools == null){ throw new Exception("Found no rows in the database table Tbl_Setting"); }
int? take = tools.CountNewsInPage;//Nullable
if(!take.HasValue)
{
// Do you want to do something if its null maybe set it to 0 and not null
take = 0;
}
string typeStr = "News";
List<Tbl_News> qGetNews = (from a in db.Tbl_News
where a.Type.Equals(typeStr)
select a).OrderByDescending(s => s.ID).Skip(skip).Take(take.Value);
return qGetNews.ToList();
}
if qGetNews is a empty list you now don't break everything after trying to iterate on it, like your return null would. instead if returning null for a lit return a new List<>() instead, gives you a more resilient result.
So I said reusable method, its more like a single action. So you work it around to this. Now you have something really reusable.
public List<Tbl_News> GetNews(string typeStr, int take, int skip = 0)
{
List<Tbl_News> qGetNews = (from a in db.Tbl_News
where a.Type.Equals(typeStr)
select a).OrderByDescending(s => s.ID).Skip(skip).Take(take);
return qGetNews.ToList();
}
Infact you shjould always try to avoid returning null if you can.
public class Rep_Setting
{
DataBase db = new DataBase();
public Tbl_Setting Tools()
{
var qGetSetting = (from a in db.Tbl_Setting
select a).FirstOrDefault();
if(qGetSetting == null){ throw new Exception("Found no rows in the database table Tbl_Setting"); }
return qGetSetting;
}
}

Resources