Android Tutorial DBUtils - Android databases are now simple!

Latest version available here: https://www.b4x.com/android/forum/threads/b4x-dbutils-2.81280/

The DBUtils code module is designed to help you integrate SQLite databases in your program.
It contains a set of methods for common SQL tasks. Issues like escaping strings and identifiers are handled automatically.
The code module is included in the attached example project.
As of version 1.07, the following methods are included:

- CopyDBFromAssets: Copies a database file stored in the APK package (added in the Files tab) to a writable location. Only copies if the file doesn't yet exist.

- CreateTable: Creates a SQL table with the given fields and types. Also sets the primary key (optionally).

- DropTable: Deletes a table.

- InsertMaps: Efficiently inserts records to a table. The data is passed as a List that contains maps as items. Each map holds the fields and their values.
Example:
B4X:
    Dim ListOfMaps As List
    ListOfMaps.Initialize
    For i = 1 To 40
        Dim m As Map
        m.Initialize
        m.Put("Id", Id)
        m.Put("First Name", "John")
        m.Put("Last Name", "Smith" & i)
        ListOfMaps.Add(m)
    Next
    DBUtils.InsertMaps(SQL, "Students", ListOfMaps)
- UpdateRecord: Updates an existing record. Accepts the field name with the new value and a map of the 'where' fields.
Example:
B4X:
    Dim WhereFields As Map
    WhereFields.Initialize
    WhereFields.Put("id", "1234")
    WhereFields.Put("test", "test #5")
    DBUtils.UpdateRecord(SQL, "Grades", "Grade", 100, WhereFields)
- ExecuteMemoryTable: Executes a SELECT query and reads all the records into a list. Each item in the list is an array of strings that represents a record in the result set.

- ExecuteMap: Executes a SELECT query and returns the first record stored in a Map object. The columns names are the keys and the values are the map values.

- ExecuteSpinner: Executes a SELECT query and fills a Spinner view (ComboBox) with the values of the first column.

- ExecuteListView: Executes a SELECT query and fills the ListView with the values of the first column and optionally of the second column.

- ExecuteJSON: Executes a SELECT query and returns a Map which you can pass to JSONGenerator to generate JSON text.
Example:
B4X:
    Dim gen As JSONGenerator 'Requires a reference to the JSON library.
    gen.Initialize(DBUtils.ExecuteJSON(SQL, "SELECT Id, [Last Name], Birthday FROM Students", Null, _
        0, Array As String(DBUtils.DB_TEXT, DBUtils.DB_TEXT, DBUtils.DB_INTEGER)))
    Dim JSONString As String
    JSONString = gen.ToPrettyString(4)
    Msgbox(JSONString, "")
See the attached example and the DBUtils code module for more information about each method.

- ExecuteHtml: Creates a nice looking html table out of the results. You can show the results using WebView. This method is very useful both for development, as it allows you to see the data and also for reports.
You can change the table style by changing the CSS variable.
New: The last parameter defines whether the values will be clickable or not.
If the values are clickable then you should catch WebView_OverrideUrl event to find the clicked cell.


- GetDBVersion / SetDBVersion: Gets or sets the database version. The value is stored in a separate table named DBVersion. This is useful to manage updates of existing databases.
This feature was implemented by corwin42. Thank you!

About the example. In this example we are creating two tables: Students and Grades. The students table lists all the students.
The grades table lists the grade of each student in each test.

Uncomment ExportToJSON or ShowTableInWebView calls to see those features.

dbutils_1.png


dbutils_3.png


To hide the table, comment the call to ShowTableInWebView in Activity_Create.

Current version: 1.20
It depends on the following libraries: JSON, RuntimePermissions, SQL and StringUtils.

Make sure to add the following snippet to the manifest editor:
B4X:
AddManifestText(
<uses-permission
  android:name="android.permission.WRITE_EXTERNAL_STORAGE"
  android:maxSdkVersion="18" />
)
See RuntimePermissions tutorial for more information: https://www.b4x.com/android/forum/threads/runtime-permissions-android-6-0-permissions.67689/#content
 

Attachments

  • DBUtils.zip
    14.4 KB · Views: 5,698
Last edited:

critalsoft

Member
Licensed User
Longtime User
Hi Erel,

I am getting following error while running DBUtils.b4a.

An error has occurred in sub: main_activity_create(B4A line 17)
SQL.Initialize(File.DirRootExternal,"1.db",True) android.database.sqlite.
SQLliteException: unable to open database file Continue?

In fact I get similar message when I try to run any program in which I try to access SQL lite db. Please guide me how to remove this error.
 

CidTek

Active Member
Licensed User
Longtime User
Try to change File.DirRootExternal to File.DirInternal. Maybe your sd card is not writable. It can happen when your device is connected to a computer in mass storage mode.

I would also use another file extension other than .db to avoid compression of the database. This is a well known problem with Android and can be avoided if you give it an extension such as png or jet which avoids compression.
 

aklisiewicz

Active Member
Licensed User
Longtime User
I see the demo App has several procedures, but when I run this it only shows the list of students, and nothing else I can do. when I press BACK button is shoots down.

Arthur
 

corwin42

Expert
Licensed User
Longtime User
I see the demo App has several procedures, but when I run this it only shows the list of students, and nothing else I can do. when I press BACK button is shoots down.

Arthur

Comment out the "ShowTableInWebView" line.
 

corwin42

Expert
Licensed User
Longtime User
Modified DBUtils 1.08

I did some small changes to the DBUtils module (and changed version to v1.08).

1. renamed cursor variables from c to cur since I regularly use a code module named C for defining constants. It's not possible to name a variable like a code module.

2. Added small and simple version control to database. With GetDBVersion() you can get the current database version and with SetDBVersion() you can set the current database version. If you release your App to public and make an update to the database later (change table structure) you will have to supply a migration function for your database so old databases get migrated to the new database structure. With the two version subs you can easily handle this. Just check in the beginning of your app, which version your database has. If it's not the current version then you can call the migration sub. A simple example how this can be done is in the example code.

@erel: Hopefully you will copy this to the first post?
 

kirbkid44

New Member
Licensed User
Longtime User
:sign0163:Would it be possible to highlight a selected row in the webview table? I am able to pull data as i need to from the table, but it would be nice for the user to see which row/column they selected. My guess is the HTML would have to be rebuilt each time the user selects a row/column, but I don't know enough about HTML or CSS to attempt that. Thanks for any help!
 

juncus

Member
Licensed User
Longtime User
.GetColumnName not defined

I'm guessing I've ommited something as I'm getting an error for each line in DBUtils that refers to cur.GetColumnName.

This does not appear to be a valid method. If I type cur.Get... it is not in the available list.

The code for DBUtils does not specify any libraries are needed but I have assumed SQL. I have version: 1.01.

Any help welcome.
 

juncus

Member
Licensed User
Longtime User
Thanks B4A version 1.2. I guess DBUtils needs a little header info saying what version it is for.
 
Last edited:

lorebarita

Member
Licensed User
Longtime User
Is it possible to add deleteRecord

DbUtils is very nice. I only noticed that deleteRecord is not in the set of functions... Any idea if you are thinking of adding it?
Thanks.
Lorenzo
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Here is the code required:
B4X:
Sub DeleteRecord(SQL As SQL, TableName As String, WhereFieldEquals As Map)
   Dim sb As StringBuilder
   sb.Initialize
   sb.Append("DELETE FROM [").Append(TableName).Append("] WHERE ")
   If WhereFieldEquals.Size = 0 Then
      Log("WhereFieldEquals map empty!")
      Return
   End If
   Dim args As List
   args.Initialize
   For i = 0 To WhereFieldEquals.Size - 1
      If i > 0 Then sb.Append(" AND ")
      sb.Append("[").Append(WhereFieldEquals.GetKeyAt(i)).Append("] = ?")
      args.Add(WhereFieldEquals.GetValueAt(i))
   Next
   Log("DeleteRecord: " & sb.ToString)
   SQL.ExecNonQuery2(sb.ToString, args)
End Sub

Example:
B4X:
   Dim m As Map
   m.Initialize
   m.Put("Test", "Test #3")
   DBUtils.DeleteRecord(SQL, "Grades", m) 'delete all records where Test = 'Test #3'
 

vasper

Member
Licensed User
Longtime User
I would also suggest an UpdateRecordMap because sometimes you need to update more fields at once on a record or a number of records.

B4X:
Sub UpdateRecordMap(SQL As SQL, TableName As String, Record As Map, WhereFieldEquals As Map)
   If WhereFieldEquals.Size = 0 Then
      Log("WhereFieldEquals map empty!")
      Return
   End If
   If Record.Size = 0 Then
      Log("Record map empty!")
      Return
   End If
   Dim sb As StringBuilder
   sb.Initialize
   sb.Append("UPDATE [").Append(TableName).Append("] SET ")
   Dim args As List
   args.Initialize
   For i = 0 To Record.Size -1
      If i > 0 Then sb.Append(",")
      sb.Append("[").Append(Record.GetKeyAt(i)).Append("] = ?")
      args.Add(Record.GetValueAt(i))
   Next
   sb.Append(" WHERE ")
   For i = 0 To WhereFieldEquals.Size - 1
      If i > 0 Then sb.Append(" AND ")
      sb.Append("[").Append(WhereFieldEquals.GetKeyAt(i)).Append("] = ?")
      args.Add(WhereFieldEquals.GetValueAt(i))
   Next
   Log("UpdateRecord: " & sb.ToString)
   SQL.ExecNonQuery2(sb.ToString, args)
End Sub

Example:

B4X:
        Dim newfieldvaluesmap As Map
        Dim wherefieldmap As Map
        newfieldvaluesmap.Initialize
   newfieldvaluesmap.Put("field1","avalue")
   newfieldvaluesmap.Put("field2","another value")
        wherefieldmap.Initialize
   wherefieldmap.Put("recordid",1)
   DBUtils.UpdateRecordMap(SQL,"tablename",newfieldvaluesmap,wherefieldmap)

I also use a lot, a function that could be included in DBUtils to get next value of an integer field:
B4X:
Sub getNextID(SQL As SQL, tablename As String, fldName As String) As Int
   Dim res As Int
   Dim resm As Map
   Dim aField As String
   aField = "idc" ' default column name used if fldName is empty. Replace/Use this for most usual identity column name.
   If Not (fldName == "") Then
      aField = fldName
   End If
   res = 1
   resm = ExecuteMap(SQL, "select max(" & aField & ") as midc from " & tablename, Null)
   If resm.IsInitialized Then
      If Not (resm.Get("midc") == Null) Then 
         res = resm.Get("midc")
         res = res + 1
      End If
   End If
   Return res
End Sub
 
Last edited:

billy047

Member
Licensed User
Longtime User
primary key with autoincrement

Hi Erel, Sometimes I need to create a table with autoincrement in the PrimaryKey, so modify the CreateTable function for optionally working with such key. Here I leave the code in case it is of interest to someone. Also add the index creation and deletion.
B4X:
Sub CreateTable(SQL As SQL, TableName As String, FieldsAndTypes As Map, PrimaryKey As String, Auto As Boolean)
   Dim sb As StringBuilder
   sb.Initialize
   sb.Append("(")
   For i = 0 To FieldsAndTypes.Size - 1
      Dim field, ftype As String
      field = FieldsAndTypes.GetKeyAt(i)
      ftype = FieldsAndTypes.GetValueAt(i)
      If i > 0 Then sb.Append(", ")
      sb.Append("[").Append(field).Append("] ").Append(ftype)
      'If field = PrimaryKey Then sb.Append(" PRIMARY KEY") 'Original line changed below
      If field = PrimaryKey Then
         sb.Append(" PRIMARY KEY")
         If Auto = True Then sb.Append(" AUTOINCREMENT")
      End If   
   Next
   sb.Append(")")
   Dim query As String
   query = "CREATE TABLE IF NOT EXISTS [" & TableName & "] " & sb.ToString
   Log("CreateTable: " & query)
   SQL.ExecNonQuery(query)
End Sub

AND Create & Delete Index

B4X:
'Create Index
Sub CreateIndex(SQL As SQL, TableName As String, IndexName As String, Fields As String, Unique As Boolean)
   Dim query, uni As String
   uni = ""
   If Unique=True Then uni = "UNIQUE"      
   query = "CREATE " & uni & " INDEX IF NOT EXISTS " & IndexName & " ON [" & TableName & "] (" & Fields & ")"
   Log("CreateIndex: " & query)
   SQL.ExecNonQuery(query)
End Sub
'Delete Index
Sub DropIndex(SQL As SQL, IndexName As String)
   Dim query As String   
   query = "DROP INDEX IF EXISTS " & IndexName 
   Log("DropIndex: " & query)
   SQL.ExecNonQuery(query)
End Sub

I hope it useful.
Guillermo
 

lorebarita

Member
Licensed User
Longtime User
DBUtils adding or updating blob field with file

Hi,
I tried to upload with DBUtils a file into a blob field but I guess am missing something. Is it possible within DBUTils?
Like something:
m.Put("attachment",attachment_path)
Thanks...
 
Top