Italian Semplice CRUD con reflection

Ciao a tutti.
Avendo una classe fatta cosi

' Class module: tCassa
Sub Class_Globals
Public db_orm_fields() As String = Array As String("idCassa", "Nome")
Public DB_ORM_Pks() As String = Array As String("idCassa")
Public idCassa As String
Public Nome As String
End Sub


In che modo potrei passare una istanza di questa classe ad un metodo per fare una insert/update a SQLite.
Pensavo ad un caosa tipo questa sotto, ma il metodo GetField mi ritorna sempre "not found".

Public Sub Insert (theO As Object)
Dim jo As JavaObject = theO
Dim fields() As String = jo.GetField("db_orm_fields")
Log("Campi:")
For Each f As String In fields
Log(f)
Next
End Sub
 

MarcoRome

Expert
Licensed User
Longtime User
A.I. Docet

B4XMainPage
B4X:
'Ctrl + click to export as zip: ide://run?File=%B4X%\Zipper.jar&Args=%PROJECT_NAME%.zip
Sub Class_Globals
    Private Root As B4XView
    Private xui As XUI
End Sub

Public Sub Initialize
End Sub

Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    B4XPages.SetTitle(Me, "Demo tCassa + SQLite")
    'Root.LoadLayout("") ' opzionale
    DB_Cassa.Initialize

    ' --- DEMO: Upsert di due righe ---
    Dim c1 As tCassa
    c1.Initialize
    c1.idCassa = "C1"
    c1.Nome = "Cassa Principale"
    DB_Cassa.Upsert(c1)

    Dim c2 As tCassa
    c2.Initialize
    c2.idCassa = "C2"
    c2.Nome = "Cassa Secondaria"
    DB_Cassa.Upsert(c2)

    ' --- DEMO: UpdateByPK della C2 ---
    c2.Nome = "Cassa Secondaria - Rinom."
    DB_Cassa.UpdateByPK(c2)

    ' --- DEMO: Get singola ---
    Dim found As tCassa = DB_Cassa.GetCassa("C1")
    If found <> Null And found.idCassa <> "" Then
        Log($"GetCassa: ${found.idCassa} - ${found.Nome}"$)
    End If

    ' --- DEMO: List all ---
    Dim tutte As List = DB_Cassa.ListCasse("")
    For Each c As tCassa In tutte
        Log($"Row: ${c.idCassa} | ${c.Nome}"$)
    Next

    ' --- DEMO: Delete e verifica ---
    DB_Cassa.DeleteCassa("C2")
    Dim afterDel As List = DB_Cassa.ListCasse("")
    Log("Dopo delete C2, righe = " & afterDel.Size)
End Sub

Class tCassa
B4X:
' Class module: tCassa
Sub Class_Globals
    Public db_orm_fields() As String = Array As String("idCassa", "Nome")
    Public DB_ORM_Pks() As String = Array As String("idCassa")
    Public idCassa As String
    Public Nome As String
End Sub

Public Sub Initialize
End Sub

' Mappa (portabile B4A/B4J/B4i)
Public Sub ToMap As Map
    Dim m As Map
    m.Initialize
    m.Put("idCassa", idCassa)
    m.Put("Nome", Nome)
    Return m
End Sub

' Riempie da Cursor (portabile)
Public Sub FromCursor(rs As ResultSet)
    idCassa = rs.GetString("idCassa")
    Nome    = rs.GetString("Nome")
End Sub

Public Sub Validate As Boolean
    Return (idCassa <> "" And Nome <> "")
End Sub

Module DB_Cassa
B4X:
' Code module: DB_Cassa
' Responsabilità: apertura DB, migrazioni, CRUD tCassa
Sub Process_Globals
    Public SQL1 As SQL
    Private sDbName As String = "appdata.db"
End Sub

Public Sub Initialize
    Dim dbPath As String = GetAppDataDir
    If File.Exists(dbPath, sDbName) = False Then
        SQL1.Initialize(dbPath, sDbName, True)
        EnsureSchema
    Else
        SQL1.Initialize(dbPath, sDbName, True)
        EnsureSchema ' idempotente
    End If
End Sub

' Percorso portabile
Private Sub GetAppDataDir As String
#If B4A
    Return File.DirInternal
#Else If B4i
    Return File.DirDocuments
#Else If B4J
    Return File.DirApp
#End If
End Sub

' Crea/Migra schema
Private Sub EnsureSchema
    SQL1.ExecNonQuery("CREATE TABLE IF NOT EXISTS tCassa (idCassa TEXT PRIMARY KEY, Nome TEXT NOT NULL)")
    SQL1.ExecNonQuery("CREATE INDEX IF NOT EXISTS idx_tCassa_Nome ON tCassa (Nome)")
End Sub

' ------- Helpers -------
Private Sub JoinList(lst As List, sep As String) As String
    Dim sb As StringBuilder
    sb.Initialize
    For i = 0 To lst.Size - 1
        If i > 0 Then sb.Append(sep)
        sb.Append(lst.Get(i))
    Next
    Return sb.ToString
End Sub

Private Sub IndexOfIgnoreCase(arr() As String, s As String) As Int
    For i = 0 To arr.Length - 1
        If arr(i).EqualsIgnoreCase(s) Then Return i
    Next
    Return -1
End Sub

' ------- Upsert "generico" sulla classe (B4A/B4J usa JavaObject; su B4i cadremo su ToMap) -------
Public Sub Upsert(obj As Object)
#If B4A Or B4J
    Dim jo As JavaObject = obj
    Dim fields() As String = jo.GetField("_db_orm_fields")
    Dim cols As List, qmarks As List, args As List
    cols.Initialize : qmarks.Initialize : args.Initialize
    For Each f As String In fields
        cols.Add(f)
        qmarks.Add("?")
        args.Add(jo.GetField("_" & f.ToLowerCase))
    Next
    Dim sqlText As String = $"INSERT OR REPLACE INTO tCassa (${JoinList(cols, ",")}) VALUES (${JoinList(qmarks, ",")})"$
    SQL1.ExecNonQuery2(sqlText, args)
    #Else If B4i
    ' in B4i niente JavaObject: usiamo ToMap
    Dim m As Map = CallSub(obj, "ToMap")
    SQL1.ExecNonQuery2("INSERT OR REPLACE INTO tCassa (idCassa, Nome) VALUES (?,?)", _
        Array(m.Get("idCassa"), m.Get("Nome")))
#End If
End Sub

' ------- Update solo colonne non-PK, WHERE PK -------
Public Sub UpdateByPK(obj As Object)
#If B4A Or B4J
    Dim jo As JavaObject = obj
    Dim fields() As String = jo.GetField("_db_orm_fields")
    Dim pks() As String    = jo.GetField("_db_orm_pks")

    Dim setParts As List, whereParts As List, args As List
    setParts.Initialize : whereParts.Initialize : args.Initialize
    
    For Each f As String In fields
        If IndexOfIgnoreCase(pks, f) = -1 Then
            setParts.Add(f & "=?")
            args.Add(jo.GetField("_" & f.ToLowerCase))
        End If
    Next
    For Each pk As String In pks
        whereParts.Add(pk & "=?")
        args.Add(jo.GetField("_" & pk.ToLowerCase))
    Next
    
    Dim sqlText As String = $"UPDATE tCassa SET ${JoinList(setParts, ", ")} WHERE ${JoinList(whereParts, " AND ")}"$
    SQL1.ExecNonQuery2(sqlText, args)
#Else If B4i
    Dim m As Map = CallSub(obj, "ToMap")
    SQL1.ExecNonQuery2("UPDATE tCassa SET Nome=? WHERE idCassa=?", _
        Array(m.Get("Nome"), m.Get("idCassa")))
#End If
End Sub

' ------- Get by id -------
Public Sub GetCassa(id As String) As tCassa
    Dim rs As ResultSet = SQL1.ExecQuery2( "SELECT idCassa, Nome FROM tCassa WHERE idCassa=?", Array As String(id))
    Dim c As tCassa
    If rs.NextRow Then
        c.Initialize
        c.FromCursor(rs)
    End If
    rs.Close
    Return c
End Sub

' ------- List all (con ricerca facoltativa per Nome) -------
Public Sub ListCasse(OptionalLikeNome As String) As List
    Dim out As List : out.Initialize
    Dim rs As ResultSet
    If OptionalLikeNome = "" Then
        rs = SQL1.ExecQuery("SELECT idCassa, Nome FROM tCassa ORDER BY Nome")
    Else
        rs = SQL1.ExecQuery2("SELECT idCassa, Nome FROM tCassa WHERE Nome LIKE ? ORDER BY Nome", Array As String("%" & OptionalLikeNome & "%"))
    End If
    Do While rs.NextRow
        Dim c As tCassa
        c.Initialize
        c.FromCursor(rs)
        out.Add(c)
    Loop
    rs.Close
    Return out
End Sub

' ------- Delete -------
Public Sub DeleteCassa(id As String)
    SQL1.ExecNonQuery2("DELETE FROM tCassa WHERE idCassa=?", Array(id))
End Sub

Manifest
B4X:
' Permission generica per Foreground Service
AddPermission(android.permission.FOREGROUND_SERVICE)

' Permission specifica per il tipo scelto (es. dataSync)
AddPermission(android.permission.FOREGROUND_SERVICE_DATA_SYNC)

' Dichiara il tipo sul service "starter"
SetServiceAttribute(starter, android:foregroundServiceType, "dataSync")
 

Attachments

  • ClasseDB.zip
    16.7 KB · Views: 7

LucaMs

Expert
Licensed User
Longtime User
A.I. Docet
Ma che te "docet", ste demenze artificiali! (Sorry, it's for Marco )

ChatGPT (not the exact words, just the gist).
"I'll prepare and send you index.html."

Me: "We said we'd use index.php."

ChatGPT: "You're right, here's the global structure:
- index.html
- ...
Do you want me to send you index.html right away?

Artificial dementia!

---------
DB_Cassa.Initialize
NOT DECLARED
 
Ok, grazie dei suggerimenti.

Volevo generalizzare anche un metodo per caricare la lista di oggetti... vedi "LoadList"
L'intento era poi di richiamare "per ogni campo nei campi restituiti dal resultset
una cosa del tipo
Dim VALUE As Object=rs.GetString(f.ToLowerCase)
jo.SetField("_" & f.ToLowerCase,VALUE)

tuttavia non so come creare nella sezione 'QUA? una instanza dell'oggetto passato baseOBJ e inizializzarlo : è possibile in B4X?





Public Sub LoadList(SQLQuery As String,args() As Object, baseOBJ As Object)
Dim out As List : out.Initialize
Dim jo As JavaObject = baseOBJ
Dim NomeTabella As String = jo.GetField("_db_orm_tabname")
If (SQLQuery="") Then
SQLQuery="SELECT * from " & NomeTabella
End If
Dim rs As ResultSet
If (args=Null) Then
rs = oSQL.ExecQuery(SQLQuery)
Else
rs = oSQL.ExecQuery2(SQLQuery,args)
End If
Do While rs.NextRow
'QUA?
Loop
rs.Close
End Sub
 
Piccolo aggiornamento... probabilmente una cosa così



Public Sub CreateInstance(TypeName As String, Params() As Object) As Object
Dim jo As JavaObject
Return jo.InitializeNewInstance(TypeName,Params).As(Object)
End Sub
Public Sub creo(theo As Object, id As Int) As Object
Dim tp=GetType(theo)
Dim nuovoObj As Object = CreateInstance(tp,Null)
Dim jo As JavaObject = nuovoObj
jo.SetField("_idcassa",id)
Return nuovoObj
End


Dim ocassa As tCassa
Dim cassa1 As tCassa=creo(ocassa,1)
Dim cassa2 As tCassa=creo(ocassa,2)
 

Sagenut

Expert
Licensed User
Longtime User
Solo una domanda per curiosità:
perchè impiccarsi ad usare Reflection e JavaObject per lavorare su un DB SQLite quando c'è una apposita libreria?
La mia non è una critica.
E' solo per capire se offre più possibilità rispetto alla libreria.
 

LucaMs

Expert
Licensed User
Longtime User
Circa un migliaio di anni fa, sviluppai un "CRUD generico", adatto a qualunque tabella (SQLite), ovviamente senza reflection né JavaObject.

Magari lo ripescherò, a meno che non ce ne siano già qui sul sito (possibile? ).
 

LucaMs

Expert
Licensed User
Longtime User
@LucaMs si quello lo avevo visto... ma mi sembrava poco "Object" ....
Nel mio caso ho già un db con una certa struttura. Punto a creare il codice delle classi per ogni tabella e poi semplici CRUD usando gli oggetti creati.
 

aeric

Expert
Licensed User
Longtime User
Ecco un esempio che utilizza MiniORM
 
Cookies are required to use this site. You must accept them to continue using the site. Learn more…