Sqlite Database: B4X Booklets
Sqlite Database: B4X Booklets
Sqlite Database: B4X Booklets
SQLite Database
To search for a given word or sentence use the Search function in the Edit menu.
All the source code and files needed (layouts, images etc.) of the example projects in this guide are
included in the SQLiteDatabase_SourceCode folder.
B4X Booklets:
B4X Getting Started
B4X Baisc Language
B4X IDE Integrated Development Environment
B4X Visual Designer
B4X CustomViews
B4X Graphics
B4X XUI B4X User Interface
B4X SQLite Database
B4X JavaObject NativeObject
1 General information
This guide covers the use of SQLite Databases in the B4X languages (B4A, B4i, B4J).
All the source code and files needed (layouts, images etc) of the example projects in this guide are
included in the SQLiteDatabase_SourceCode folder.
There are three folders for each project, one for each platform B4A, B4i and B4J.
The interface between your program and the database is the SQL language.
The data is stored in tables, each table has a certain number of columns and rows.
Each row contains a data set and the different data of a given set are stored in the columns.
Simple example programs are included in the SourceCode\SQL folder.
1 General information 6 B4X SQLite Database
1.1 B4X
• B4A Android
B4A includes all the features needed to quickly develop any type of Android app.
• B4i iOS
B4J is a 100% free development tool for desktop, server and IoT solutions.
With B4J you can easily create desktop applications (UI), console programs (non-UI) and
server solutions.
The compiled apps can run on Windows, Mac, Linux and ARM boards (such as Raspberry
Pi).
B4R is a 100% free development tool for native Arduino and ESP8266 programs.
B4R follows the same concepts of the other B4X tools, providing a simple and powerful
development tool.
B4R, B4A, B4J and B4i together make the best development solution for the Internet of
Things (IoT).
2 SQLite Database 7 B4X SQLite database
2 SQLite Database
If you add a default database to your project in the files Tab, it is located in the DirAssets
folder. Databases cannot be accessed in DirAssets even if it's only for reading.
Therefore, you must copy it to another folder.
Example in the SQLiteLight1 programs.
B4A
With DirRootExternal you can also add a subdirectory.
For example: DirRootExternal & "/MyDatabase"
Don't forget to create the subdirectory: File.MakeDir(File.DirRootExternal, "MyDatabase")
B4i
B4J
B4A, B4i
SQL1.Initialize(DBDirName, DBFileName, True)
B4J
And:
SQL1.InitializeSQLite(DBDirName, DBFileName, True)
You can create a database in a SQLite program on the PC or you can create it in the code like
below.
SQL identifiers are case insensitive. You could use for example:
SQL1.ExecNonQuery("CREATE TABLE TableName(col1 integer, col2 text, col3 Real")
But in B4A, SQL.GetString(ColumnName), ColumnName is case sensitive!
The column names must be spelled exactly the same name as in the table creation.
With the example above, col1 works but Col1 will through an error.
2.1 SQLite Database basics 10 B4X SQLite database
INTEGER PRIMARY KEY is a special data type which is unique and will never change.
You can define a specific column dedicated to the PRIMARY KEY.
But this is not mandatory, SQLite has an internal column named rowid which can be used.
This is used in the SQLiteLight examples.
Each time you add a new record the PRIMARY KEY is incremented by 1.
When you delete a record the PRIMARY KEY of this record is lost.
When you load a database, and display it in a table be aware that the row indexes in the table are not
the same as the database rowids. Therefore, you must read and memorize the PRIMARY KEYs
somewhere to know which record is in which line.
Comparison:
• Creation.
o With a specific ID column.
"CREATE TABLE persons (ID INTEGER PRIMARY KEY, FirstName TEXT, LastName
TEXT, City TEXT)"
o With no specific ID column.
"CREATE TABLE persons (FirstName TEXT, LastName TEXT, City TEXT)"
• Reading.
o With a specific ID column.
"SELECT ID, FirstName AS [First name], LastName AS [Last name], City FROM
persons"
o With no specific ID column.
Reads the PRIMARY Key in the query.
"SELECT rowid AS ID, FirstName AS [First name], LastName AS [Last name],
City FROM persons"
Doesn’t read the PRIMARY Key in the query.
"SELECT FirstName AS [First name], LastName AS [Last name], City FROM
persons"
Note: If you use this query "SELECT * FROM persons" the rowid column is not
included. If you want it, you must specify it like in the examples above.
Read it like this "SELECT rowid, * FROM persons" or read it in a separate query.
• Inserting.
o With a specific ID column.
"INSERT INTO persons VALUES (NULL, ‘John’, ‘KERRY’, ‘Boston’)"
You must use NULL for the PRIMARY KEY column.
o With no specific ID column.
"INSERT INTO persons VALUES (‘John’, ‘KERRY’, ‘Boston’)"
2.1 SQLite Database basics 11 B4X SQLite database
Or
SQL1.ExecNonQuery2("INSERT INTO TableName VALUES (?, ?, ?)" Array As Object(Val1, Val2,
Val2))
SQL1.ExecNonQuery2("INSERT INTO TableName VALUES (?, ?, ?)" Array As Object(12, “John”,
235))
UPDATE TableName Set Col1 = Val1, Col2 = ‘Val2’, Col3 = Val3 WHERE ID = idVal
SQL1.ExecNonQuery("UPDATE TableName Set Col1 = Val1, Col2 = 'Val2', Col3 = Val3 WHERE
ID = idVal")
Again, a text variable must be between two quotes like 'Val2’, numbers not like Val1 and Val3.
Or
SQL1.ExecNonQuery2("UPDATE TableName Set Col1 = ?, Col2 = ?, Col3 = ? WHERE ID = ?"
Array As Object(Val1, Val2, Val3, idVal))
Here no need to care with the quotes for text variables!
The SELECT statement is used to query the database. The result of a SELECT is zero or more rows
of data where each row has a fixed number of columns.
A SELECT statement does not make any changes to the database.
Examples:
• A single column
SELECT Col1 FROM TableName
ResultSet1 = SQL1.ExecQuery("SELECT Col1 FROM TableName")
• Max / min value in a column, in the examples the max and min values of the given column.
SELECT max(Col1) FROM TableName
SELECT min(Col1) FROM TableName
Max = SQL1.ExecQuerySingleResult("SELECT max(Col1) FROM TableName")
Min = SQL1.ExecQuerySingleResult("SELECT min(Col1) FROM TableName")
2.1 SQLite Database basics 13 B4X SQLite database
There exist also a sum() function, but it’s better to use total().
If there is a row with a Null value, sum() returns Null, but Total() returns 0!
Some functions:
• sum() Calculates the sum of a column.
• avg() Calculates the average of a column.
• min() Calculates the min value of column.
• max() Calculates the min value of column.
• length() Calculates the number of characters of a string or the number of characters of
the string representation of a number.
• lower() Returns a string in lower case characters.
• upper() Returns a string in upper case characters.
• substr() Returns a sub string.
• typeof() Returns the data type of a column.
More details can be found in the SQLite documentaion here: Core Functions
and here: Expressions
and here: Date And Time Functions
2.1 SQLite Database basics 14 B4X SQLite database
After the SELECT expression you can add a WHERE expression for filtering.
The WHERE expression is evaluated for each row in the input data as a Boolean expression. Only
rows for which the WHERE clause expression evaluates to true are included from the dataset before
continuing. Rows are excluded from the result if the WHERE clause evaluates to either false or
NULL.
Examples:
• A single row.
Where the rowid has the value of the numeric variable idVal
SELECT * FROM TableName WHERE rowid = idVal
ResultSet1 = SQL1.ExecQuery("SELECT * FROM TableName WHERE rowid = “ & idVal)
Where an ID column has the value of the variable idVal
SELECT * FROM TableName WHERE ID = idVal
ResultSet1 = SQL1.ExecQuery("SELECT * FROM TableName WHERE ID = “ & idVal)
If a SELECT statement that returns more than one row does not have an ORDER BY clause, the
order in which the rows are returned is undefined.
Or, if a SELECT statement does have an ORDER BY clause, then the list of expressions attached to
the ORDER BY determine the order in which rows are returned to the user.
The column to order can be given either by its name Col1 or its number 2.
The column numbering begins with 1.
SQLite has no specific Date/Time data taype, but has several Date/Ttime functions.
For more details, examples and what timestring and modifiers are, please look at the SQLite
documentation.
In B4X the best way to store dates is to store them as ticks, which are the number of milliseconds
since January 1, 1970.
SQLite doesn’t have the same ticks but has "unixepoch" ticks which are the number of seconds
since January 1, 1970.
The date format of the data() function is yyyy-MM-dd there is no possibility to change this format
with ‘unixepoch’
Another solution could be to store the date as a String with yyyy-MM-dd or yyyy-MM-dd HH.mm
format.
Only these formats must be used if you want tu use the functions below.
And when you read the data you can return it with a format with the strftime function.
strftime(format, timestring, modifier, modifier, ...)
format can be:
%d day of month: 00
%f fractional seconds: SS.SSS
%H hour: 00-24
%j day of year: 001-366
%J Julian day number
%m month: 01-12
%M minute: 00-59
%s seconds since 1970-01-01
%S seconds: 00-59
%w day of week 0-6 with Sunday==0
%W week of year: 00-53
%Y year: 0000-9999
In a table with a column Part of type TEXT Number of type INTEGER and another column Price
of type REAL
SELECT typeof(Part), typeof(Number), typeof(Price) FROM TableName
ResultSet1 = SQL1.ExecQuery("SELECT typeof(Part), typeof(Number), typeof(Price) FROM
TableName”)
Get the data type with:
Column request or other request > result
Part: Cursor1.GetString(“Part”) or Cursor1.GetString2(0) > text
Number: Cursor1.GetString(“Number”) or Cursor1.GetString2(1) > integer
Price: Cursor1.GetString(“Price”) or Cursor1.GetString2(2) > real
For a string, the returned value is the number of characters not the number of bytes.
For a number, the returned value is the number of characters of its string representation.
For a blob, the returned value is the number of bytes.
SELECT max(length(Col1)) FROM TableName
MaxChars = SQL1.ExecQuerySingleResult("SELECT max(length(Col1)) FROM
TableName")
The substr(String, BeginIndex, Lenght) function returns a sub sting of String beginning with the
character at the BeginIndex position and with the number of characters given in Lenght.
If Lenght is omitted, substr returns the sub string from BeginIndex to the end of the string.
The index of the first character is 1.
Exampl:
Get the year from a date string ‘31/11/2016’
SELECT substr(Date, 7, 4) AS Year FROM TableName
ResultSet1 = SQL1.ExecQuery("SELECT substr(Date,7,4) AS Year FROM TableName")
The replace(String, Target, Replace) function returns a string formed by substituting string Replace
for every occurrence of string Target in String. The replace function is case sensitive.
Equivalent to MyText.Replace(SubString) in B4X.
Example:
In a date like 2016-12-31 replace ‘–‘ by ‘/’ to get 2016/12/31
ResultSet1 = SQL1.ExecQuery("SELECT replace(Date,’-’,’/’) AS Date FROM TableName")
Example:
The round(Number, Digits) function returns a floating-point value Number rounded to Digits digits
to the right of the decimal point. If the Digits argument is omitted, it is assumed to be 0.
Equivalent to Round2(Number, Digits) in B4X.
Example:
Gets the value in the column Number and rounds it to two decimals and sets the column alias to
Value.
ResultSet1 = SQL1.ExecQuery("SELECT round(Number,2) AS Value FROM TableName")
2.1.10.10 Get the number of database rows that were changed changes()
Get the number of database rows that were changed or inserted or deleted by the most recently
completed INSERT, DELETE, or UPDATE.
SELECT changes() FROM TableName
NbChanges = SQL1.ExecQuerySingleResult("SELECT changes() FROM TableName")
IDList.Initialize
ResultSet1 = SQL1.ExecQuery("SELECT rowid FROM TableName")
Do While ResultSet1.NextRow
IDList.Add(Cursor1.GetLong2(0))
Loop
2.1 SQLite Database basics 21 B4X SQLite database
Examples:
Do While ResultSet1.NextRow
RowIDList.Add(ResultSet1.GetInt2(0))
Loop
Do While ResultSet1.NextRow
ltvFirstName.Add(ResultSet1.GetString2(0))
ltvLastName.Add(ResultSet1.GetString2(1))
ltvCity.Add(ResultSet1.GetString2(2))
Loop
The following methods extract the different data from the Cursor.
• ResultSet.GetInt returns an Integer value.
• ResultSet.GeLong returns a Long value.
• ResultSet.GetDouble returns a Double value.
• ResultSet.GetString returns a String value.
• ResultSet.GetBlob returns a Binary Large Object, used for images.
ResultSet.GetString(FirstName) ResultSet.GetString2(0)
2.1 SQLite Database basics 22 B4X SQLite database
The data is organized in rows each row contains the data for each column defined in the query.
Example:
Cursor1 = Starter.SQL1.ExecQuery("SELECT FirstName As [First name], LastName As [Last
name], City FROM persons")
Each row holds 3 values, one for each column.
The following methods extract the different data from the Cursor.
• Cursor.GetInt returns an Integer value.
• Cursor.GeLong returns a Long value.
• Cursor.GetDouble returns a Double value.
• Cursor.GetString returns a String value.
• Cursor.GetBlob returns a Binary Large Object, used for images.
Cursor.GetString(FirstName) Cursor.GetString2(0)
2.1 SQLite Database basics 23 B4X SQLite database
• Sets the values of the rows in a column to a given new value where the value is another old
value.
UPDATE TableName SET ColName = 'ValueNew' WHERE ColName = 'ValueOld'
SQL1.ExecNonQuery("UPDATE TableName SET ColN = 'ValueNew' WHERE ColN = 'ValueOld'")
The DROP TABLE statement removes a table added with the CREATE TABLE statement. The
name specified is the table name. The dropped table is completely removed from the database
schema and the disk file. The table can not be recovered. All indices and triggers associated with
the table are also deleted.
The optional IF EXISTS clause suppresses the error that would normally result if the table does not
exist.
Sub InsertBlob
'convert the image file to a bytes array
Private InputStream1 As InputStream
InputStream1 = File.OpenInput(File.DirAssets, "smiley.gif")
Private OutputStream1 As OutputStream
OutputStream1.InitializeToBytesArray(1000)
File.Copy2(InputStream1, OutputStream1)
Private Buffer() As Byte 'declares an empty array
Buffer = OutputStream1.ToBytesArray
Here we are using a special type of OutputStream which writes to a dynamic byte array.
File.Copy2 copies all available data from the input stream into the output stream.
Then the bytes array is written to the database.
Sub ReadBlob
Private ResultSet1 As ResutlSetr
'Using ExecQuery2 is safer as it escapes special characters automatically.
'In this case it doesn't really matter.
ResultSet1 = SQL1.ExecQuery2("SELECT image FROM table2 WHERE name = ?", Array As
String("smiley"))
ResultSet1.NextRow
Private Buffer() As Byte 'declare an empty byte array
Buffer = ResultSet1.GetBlob("image")
Private InputStream1 As InputStream
InputStream1.InitializeFromBytesArray(Buffer, 0, Buffer.Length)
• ExecQuery(Query As String)
Executes the query, you must take care of the datatype.
Example:
ResultSet1 = SQL1.ExecQuery("SELECT * FROM TableName WHERE Col1 = '" & MyText &
"' AND Col2 >= " & minVal & " AND Col2 <= " & maxVal)
Note that MyText is between two quotes because the data field is a TEXT field!
Note that ExecQuery2 is safer because it takes care of the column data type!
Note that with ExecQuery and text, you need to put the text between quotes like ‘text’.
With ExecQuery2 and text, you must not use the quotes, ExecQuery2 takes care of it.
• ExecNonQuery(Query As String)
Executes the query, you must take care of the datatype.
Example:
SQL1.ExecNonQuery("INSERT INTO table1 VALUES('abc', 1, 2)")
Note that abc is between two quotes because the data field is a TEXT field!
Note that ExecQuery2 is safer because it takes care of the column data type!
Sub InsertManyRows
SQL1.BeginTransaction
Try
For i = 1 To 500
SQL1.ExecNonQuery2("INSERT INTO table1 VALUES ('def', ?, ?)", Array As Object(i,
i))
Next
SQL1.TransactionSuccessful
Catch
Log(LastException.Message)
End Try
SQL1.EndTransaction
End Sub
This code is an example of adding many rows. Internally a lock is acquired each time a "writing"
operation is done.
By explicitly creating a transaction the lock is acquired once.
The above code took less than half a second to run on a real device.
Without the BeginTransaction / EndTransaction block it took about 70 seconds.
A transaction block can also be used to guarantee that a set of changes were successfully done.
Either all changes are made, or none are made.
By calling SQL.TransactionSuccessful we are marking this transaction as a successful transaction.
If you omit this line, all the 500 INSERTS will be ignored.
It is very important to call EndTransaction eventually.
SQL1.BeginTransaction
Try
'Execute the sql statements.
SQL1.TransactionSuccessful
Catch
'the transaction will be cancelled
End Try
SQL1.EndTransaction
2.1 SQLite Database basics 28 B4X SQLite database
The SQL library supports asynchronous select queries and asynchronous batch inserts.
Asynchronous means that the task will be processed in the background and an event will be raised
when the task completes. This is useful when you need to issue a slow query and keep your
application responsive.
The first parameter is the "event name". It determines which sub will handle the QueryComplete
event.
Dim rs as ResultSet
Dim SenderFilter As Object = SQL1.ExecQueryAsync("SQL", "SELECT * FROM table1", Null)
Wait For (SenderFilter) SQL_QueryComplete (Success As Boolean, rs As ResultSet)
If Success Then
Do While rs.NextRow
Log(rs.GetInt2(0))
Loop
rs.Close
Else
Log(LastException)
End If
2.1 SQLite Database basics 29 B4X SQLite database
For i = 1 To 10000
SQL1.AddNonQueryToBatch("INSERT INTO table1 VALUES (?)", Array As Object(Rnd(0,
100000)))
Next
SQL1.ExecNonQueryBatch("SQL")
...
Sub SQL_NonQueryComplete (Success As Boolean)
Log("NonQuery: " & Success)
If Success = False Then Log(LastException)
End Sub
Since B4A 7.00, B4i 4.00 and B4J 5.50 you should use this code using a resumable sub.
For i = 1 To 1000
SQL1.AddNonQueryToBatch("INSERT INTO table1 VALUES (?)", Array(Rnd(0, 100000)))
Next
Dim SenderFilter As Object = SQL1.ExecNonQueryBatch("SQL")
Wait For (SenderFilter) SQL_NonQueryComplete (Success As Boolean)
Log("NonQuery: " & Success)
2.2 SQLite Database Multiple tables 30 B4X SQLite database
In the table Stock we use the ID of the product rather than its name.
The same for the Supplier in the Products table.
The query involves the three tables Stock, Products and Suppliers:
FROM Stock, Products, Suppliers
If you have to do many inserts into a database, you should use BeginTransaction and
EndTransaction this will considerably speed up the process.
Typical usage:
SQL1.BeginTransaction
Try
'block of statements like:
For i = 1 To 1000
SQL1.ExecNonQuery("INSERT INTO table1 VALUES(...)
Next
SQL1.TransactionSuccessful
Catch
Log(LastException.Message) 'no changes will be made
End Try
SQL1.EndTransaction
2.4 SQLite Database First steps 33 B4X SQLite database
First reference the SQL library in the Libs Tab in the lower right corner in the IDE.
Declare it with Public in the Process_Globals routine of the Main module or for B4A in the
Process_Globals routine of the Starter Service module.
I suggest, to define two other variables for the database path and file name:
Sub Process_Globals
Public SQL1 As SQL
Public SQLDataBasePath As String
Public SQLDataBaseName As String
B4A: In the Starter Module or in the Main module if you use only one Activity.
SQLDataBasePath = File.DirInternal
SQLDataBaseName = "persons.db"
B4A
Initialize it in the Service_Create routine in the Starter Service.
Or in the Main module with If FirstTime Then / End If
If you already have a database in the Files folder of the project you need to copy it from
File.DirAssets in another folder.
Databases are NOT accessible from File.DirAssets even for reading only!
B4i
SQLDataBasePath = File.DirDocuments
SQLDataBaseName = "persons.db"
' File.Delete(SQLDataBasePath, "persons.db") ' only for testing, removes the database
B4J
In the #Region Project Attributes you must reference the SQLite jar file :
SQLDataBasePath = File.DirData("jSQLiteLight1")
SQLDataBaseName = "persons.db"
This example programs are very simple projects with a very simple user interface.
The goal is to show how to use SQLite with as much as possible the same code for the three
operating systems and not optimizing the layouts nor use operating system specific layouts.
The database name, the table name and the column names are hard coded, to make the code better
readable.
It is self-explanatory.
2.5.1.1.1 Process_Global
We dim the process global variables.
Sub Process_Globals
Public SQL1 As SQL
Public RowIDList As List ' list containing the IDs of the database
' we need it because the rowids can be different from the list indexes
' if we delete an entry its rowid is lost
End Sub
2.5.1.1.2 Globals
We dim all the views of the layout.
Sub Globals
Private lblRowID As Label
Private edtFirstName, edtLastName, edtCity As EditText
End Sub
2.5.1.1.3 Activity_Create
We check if the database already exists, initialize it, load it and show the first entry.
2.5.1.1.4 Activity_Resume
If the database is not initialized we initialize it, initialize the IDList list, read the database and show
the first entry.
Sub Activity_Resume
RowIDList.Initialize 'initialize the ID list
ReadDataBase 'read the database
ShowEntry(0) 'show the first entry
End Sub
2.5.1.1.5 Activity_Pause
Program closing:
2.5.1.2.1 Process_Globas
We dim the process global variables.
Sub Process_Globals
Public App As Application
Public NavControl As NavigationController
Private Page1 As Page
Private HUD1 As HUD ' HUD library, used to display Toastmessages
Public RowIDList As List ' list containing the RowIDs of the database
' we need it because the IDs can be different from the list indexes
' if we delete an entry its ID is lost
End Sub
2.5.1.2.2 Application_Start
Private Sub Application_Start (Nav As NavigationController)
'**** opreating system specific code
NavControl = Nav
Page1.Initialize("Page1")
Page1.Title = "iSQLiteLight1"
Page1.RootPanel.Color = Colors.White
Page1.RootPanel.LoadLayout("main")
NavControl.ShowPage(Page1)
2.5.1.2.3 Page_Resize
Private Sub Page1_Resize(Width As Int, Height As Int)
' read the databes
ReadDataBase
CurrentIndex = 0
ShowEntry(CurrentIndex)
End Sub
2.5 First example program 41 B4X SQLite database
2.5.1.3.1 Process_Globals
We dim the process global variables.
Sub Process_Globals
'operating system objecte
Private fx As JFX
Private MainForm As Form
Private lblToastMessage As Label
Public ToastMessageTimer As Timer
Public RowIDList As List ' list containing the RowIDs of the database
' we need it because the IDs can be different from the list indexes
' if we delete an entry its ID is lost
End Sub
2.5.1.3.2 AppStart
Sub AppStart (Form1 As Form, Args() As String)
'**** opreating system specific code
MainForm = Form1
MainForm.RootPane.LoadLayout("Main") 'Load the layout file.
MainForm.Show
MainForm.Title = "jSQLiteLight1"
ToastMessageTimer.Initialize("ToastMessageTimer", 1000)
RowIDList.Initialize
'initialize the ID list
2.5.2.2 ReadDataBase
We
- Define a ResultSet and read the rowids from the database.
- Check if the database is not empty.
- Fill IDList with the rowids of all entries.
- Set the current index to 0
- Close the ResultSet
2.5.2.3 ShowEntry
We get the selected entrys rowid from IDList, read the entry from the database, fill the EditText
views and close the ResultSet.
RowID = RowIDList.Get(EntryIndex) 'get the RowID for the given entry index
'read the entry with the given RowID
ResultSet1 = SQL1.ExecQuery("SELECT * FROM persons WHERE rowid = " & RowID)
lblRowID.Text = RowID 'display the RowID
ResultSet1.NextRow 'set the next row
edtFirstName.Text = ResultSet1.GetString("FirstName") 'read the FirstName column
edtLastName.Text = ResultSet1.GetString("LastName") 'read the LasstName column
edtCity.Text = ResultSet1.GetString("City") 'read the City column
ResultSet1.Close 'close the ResultSet, we don't it anymore
End Sub
2.5 First example program 44 B4X SQLite database
2.5.2.4 AddEntry
'to display the ID of the last entry we read the max value of the internal 'rowid' column
RowID = SQL1.ExecQuerySingleResult("SELECT max(rowid) FROM persons")
RowIDList.Add(RowID) 'add the last ID to the list
CurrentIndex = RowIDList.Size - 1 'set the current index to the last one
lblRowID.Text = RowID 'display the last index
End If
ResultSet1.Close 'close the ResultSet, we don't it anymore
End Sub
• B4i
Msgbox("One or more data is missing", "Missing data")
HUD1.ToastMessageShow("Entry added", False) ' confirmation for the user
2.5.2.5 DeleteEntry
We ask the user if he really wants to delete the selected entry.
If the answer is yes then we delete it.
And set the new CurrentIndex.
Sub DeleteEntry
Private Query As String
Private Answ As Int
• B4i
HUD1.ToastMessageShow("Entry added", False) ' confirmation for the user
2.5.2.6 UpdateEntry
We use ExecNonQuery2 instead of ExecNonQuery because it’s easier, we don’t need to take care
of the data type.
The ? sign is a placeholder for the data which must be given in the array.
Sub UpdateEntry
Private Query As String
• B4i
HUD1.ToastMessageShow("Entry added", False) ' confirmation for the user
2.6 SQLiteLight2 Second program 46 B4X SQLite database
The user interfaces are somewhat different between the thre operating systems.
I prefered making the user interfaces more operation system like with specific objects, rather than
making them almost the same, especially for B4A and B4i
2.6 SQLiteLight2 Second program 47 B4X SQLite database
Only the SQLite related routines are shown. Operating system routines are not shown.
We declare operating system dependant variables either in Process_Globals (B4i, B4J) or Globals
in B4A.
Sub Process_Globals
' different operating system variables
'
'
'operating system indepenant variables
Public SQL1 As SQL
Query = "SELECT FirstName As [First name], LastName As [Last name], City FROM
persons"
'depending if the filter is active or not we add the filter query at the end of the
query
'the filter query is defined in the Filter Activity
If Filter.flagFilterActive = False Then
btnFilter.Text = "Filter" 'change the text in the Filter button
Else
Query = Query & Filter.Query
btnFilter.Text = "UnFilter"'change the text in the Filter button
End If
'displays the database in a table
wbvTable.LoadHtml(ExecuteHtml(SQL1, Query, Null, True))
ReadDataBaseIDs
End Sub
2.6 SQLiteLight2 Second program 50 B4X SQLite database
This routine generates the Html string for the LoadHtml method.
It is extracted from the DBUtils class.
It is the same for all three operating systems.
sb.Append("</tr>").Append(CRLF)
2.6.1.4 ReadDatabaseRowIDs
We read the rowids from the database. We need this because the entry numbering is not straigt
forward. If we delete an entry with a given rowid this one is lost to maintain all the other rowids the
same.
The routine is the same for all three operating systems.
'We read only the ID column and put them in the IDList
RowIDList.Initialize 'initialize the
ID list
Do While ResultSet1.NextRow
RowIDList.Add(ResultSet1.GetInt2(0)) 'add the rowid's to the RowID list
Loop
If RowIDList.Size > 0 Then
CurrentIndex = 0 'set the current index
to 0
Else
CurrentIndex = -1 'set the current index
to -1, no selected item
ToastMessageShow("No items found", False)
End If
ResultSet1.Close
'close the ResultSet, we don't need it anymore
End Sub
2.6.1.5 UpdateSelectedEntryDisplay
Query = "SELECT FirstName, LastName, City FROM persons WHERE rowid = " &
RowIDList.Get(CurrentIndex)
ResultSet1 = SQL1.ExecQuery(Query)
ResultSet1.NextRow
lblSelectedItem.Text = ResultSet1.GetString("FirstName") & " " &
ResultSet1.GetString("LastName") & " " & ResultSet1.GetString("City")
ResultSet1.Close
End Sub
2.6 SQLiteLight2 Second program 52 B4X SQLite database
The URL variable holds the return value from the WebView event.
It could look like this http//2.7.stub/ where 2 is the col index and 7 is the row index.
The col and row values are extracted in values = Regex.Split("[.]", Url.SubString(7))
values(0) holds the col value
values(1) holds the row value
values(2) holds the end of the string
The Location string holds the return value from the WebView event.
2.6 SQLiteLight2 Second program 53 B4X SQLite database
The code is shown for one objcet only, the principle is the same for the others.
2.6.3.1 B4A
'We execute a query for each column and fill the Spinner
'We use SELECT DISTINCT to have each existing first name in the database only once
Query1 = "SELECT DISTINCT FirstName FROM persons ORDER BY FirstName ASC"
2.6.3.2 B4i
We use Pickers.
'We execute a query for each column and fill the Spinner
'We use SELECT DISTINCT to have each existing first name in the database only once
'we fill the Picker with the data from the database
Do While ResultSet1.NextRow
lst.Add(ResultSet1.GetString("FirstName"))
Loop
picFirstName.SetItems(0, lst)
2.6.3.3 B4J
We use ComboBoxes.
'We execute a query for each column and fill the ComboBox
'We use SELECT DISTINCT to have each existing first name in the database only once
Query1 = "SELECT DISTINCT FirstName FROM persons ORDER BY FirstName ASC"
This program is almost the same as SQLiteLight2, all functions are the same.
The differences are the database path, database name, table name, column number, column names,
column alias names and column data types are variables instead being hard coded.
Sub ShowTable
Private i As Int
Private Query As String
Query = "SELECT FirstName As [First name], LastName As [Last name], _
City FROM persons"
Sub ShowTable
Private i As Int
Private Query As String
This SQLite example program, SQLiteLight4, is a bit more elaborated than SQLiteLight2.
In SQLiteLight2 there is only one table, in this program there are three tables.
The purpose of this example is to show the principle of managing several tables.
To make the code more readable, all names are hard coded and not stored in variables like in
SQLiteLight3.
The program manages a spare part stock. The tables are intentionally very simple with just a few
columns and not all possible errors or mistakes a user can make are checked to keep the code simple
and easier to read and understand.
In the table Stock we use the ID of the product rather its name. The same in the table Products for
the Supplier. The advantage is that we memorize a reference to the data in the original table instead
of copying the data into another table. If we change once the data in the original table all the data in
other tables are updated automatically.
The query concerns the three tables Stock, Products and Suppliers:
FROM Stock, Products, Suppliers
There is a B4A SQLiteViewer program in the forum, that allows you to load and display databases.
The program uses the DBUtils module and the table is shown in a WebView view.
The usage of the DBUtils module is explained in the DBUtils chapter.
3 DBUtils 2 59 B4X SQLite database
3 DBUtils version 2
For those who are not familiar with SQLite, Erel has written DBUtils code modules that should
make things easier.
The target folder is returned. If the database file already exists, then no copying is done.
The target folder is returned. If the database file already exists, then no copying is done.
GetDBFolder As String
Returns the path to a folder where you can create a database, preferably on the secondary storage.
3.2 Examples
SQLiteDatabase_SourceCode\DBUtilsDemo\B4A
SQLiteDatabase_SourceCode\DBUtilsDemo \B4i
SQLiteDatabase_SourceCode\DBUtilsDemo \B4J.
Most of the code is the same for all three products B4A, B4i and B4J.
The code is not explained in detail, I think that it is enough self explanatory.
3 DBUtils 2 66 B4X SQLite database
B4J
B4A B4i
3 DBUtils 2 67 B4X SQLite database
The main code differences between the three products are the start of the program which are
operating system specific and the WebView event routines.
The WebView event routines are the same in B4A and B4i but different in B4J