GRDB pour SQLite
muqaddar
Administrateur
Une bonne alternative à FMDB pour Swift pour ceux qui utilisent SQLite mais pas CoreData ?
https://github.com/groue/GRDB.swift
(bien que FMDB soit maintenant compatible avec Swift)
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Moi je suis satisfait de FMDB en Objective C et je suppose que ça doit être la même chose sur Swift.
Je ne vois pas de raison de changer
Bonjour,
Je suis l'auteur de GRDB.swift.
Les utilisateurs de fmdb se sentiront à l'aise avec GRDB.
Ils trouveront peut-être que charger lignes et valeurs sera plus facile avec GRDB, parce qu'au lieu d'itérer à la main FMResultSet, et de les fermer à la main quand on ne veut qu'une seule ligne, GRDB charge des collections standard de Swift, ou même de simples valeurs comme Int, String, ou NSDate.
Ensuite, GRDB gère proprement les erreurs de SQLite, quand fmdb ne respecte pas les conventions classiques autour de NSError.
Ceci dit, GRDB a été grandement inspiré par fmdb: je ne suis qu'un nain sur les épaules d'un géant !
Est-ce qu'il peut y avoir une différence de performance sur une itération de 5000 lignes contenant une vingtaine de champs (c'est arbitraire) entre FMDB et GRDB ? Puisque les méthodes ne semblent pas les mêmes.
Salut muqaddar.
Bon, voilà les choses en l'état.
Le code ci-dessous tourne, sur ma machine, en:
- GRDB: 0.319s (3% STDEV)
- FMDB: 0.070s (4% STDEV)
Donc l'avantage est clairement à FMDB. J'ai encore un peu de boulot.
class FMDBBenchmarksTests: XCTestCase {
func testGRDBPerformanceExample() {
let path = "/tmp/GRDB.sqlite"
do { try NSFileManager.defaultManager().removeItemAtPath(path) } catch { }
let dbQueue = FMDatabaseQueue(path: path)
dbQueue.inDatabase { db in
db.executeUpdate("CREATE TABLE items (i0 INT, i1 INT, i2 INT, i3 INT, i4 INT, i5 INT, i6 INT, i7 INT, i8 INT, i9 INT)", withArgumentsInArray: nil)
for i in (0..<10000) {
db.executeUpdate("INSERT INTO items (i0, i1, i2, i3, i4, i5, i6, i7, i8, i9) VALUES (?,?,?,?,?,?,?,?,?,?)", withArgumentsInArray: [i % 3, i % 4, i % 5, i % 6, i % 7, i % 8, i % 9, i % 10, i % 11, i % 12])
}
}
// This is an example of a performance test case.
self.measureBlock {
var sum: Int64 = 0
dbQueue.inDatabase { db in
if let rs = db.executeQuery("SELECT * FROM items", withArgumentsInArray: nil) {
while rs.next() {
let i0 = rs.longLongIntForColumn("i0")
let i1 = rs.longLongIntForColumn("i1")
let i2 = rs.longLongIntForColumn("i2")
let i3 = rs.longLongIntForColumn("i3")
let i4 = rs.longLongIntForColumn("i4")
let i5 = rs.longLongIntForColumn("i5")
let i6 = rs.longLongIntForColumn("i6")
let i7 = rs.longLongIntForColumn("i7")
let i8 = rs.longLongIntForColumn("i8")
let i9 = rs.longLongIntForColumn("i9")
sum += i0 + i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9
}
}
}
NSLog("FMDB sum: \(sum)")
}
}
}
func testGRDBPerformanceExample() {
let path = "/tmp/GRDB.sqlite"
do { try NSFileManager.defaultManager().removeItemAtPath(path) } catch { }
let dbQueue = try! DatabaseQueue(path: path)
try! dbQueue.inDatabase { db in
try db.execute("CREATE TABLE items (i0 INT, i1 INT, i2 INT, i3 INT, i4 INT, i5 INT, i6 INT, i7 INT, i8 INT, i9 INT)")
for i in (0..<10000) {
try db.execute("INSERT INTO items (i0, i1, i2, i3, i4, i5, i6, i7, i8, i9) VALUES (?,?,?,?,?,?,?,?,?,?)", arguments: [i % 3, i % 4, i % 5, i % 6, i % 7, i % 8, i % 9, i % 10, i % 11, i % 12])
}
}
// This is an example of a performance test case.
self.measureBlock {
var sum: Int64 = 0
dbQueue.inDatabase { db in
for row in Row.fetch(db, "SELECT * FROM items") {
let i0: Int64 = row.value(named: "i0")!
let i1: Int64 = row.value(named: "i1")!
let i2: Int64 = row.value(named: "i2")!
let i3: Int64 = row.value(named: "i3")!
let i4: Int64 = row.value(named: "i4")!
let i5: Int64 = row.value(named: "i5")!
let i6: Int64 = row.value(named: "i6")!
let i7: Int64 = row.value(named: "i7")!
let i8: Int64 = row.value(named: "i8")!
let i9: Int64 = row.value(named: "i9")!
sum += i0 + i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9
}
}
NSLog("GRDB sum: \(sum)")
}
}
}
Merci pour le test.
Je ne sais pas comment marche row.value().
Avec FMDB je me souviens de très grosses différences de performances entre l'appel de l'index du champ et l'appel du nom du champ lui-même. Donc je pense qu'il peut encore être plus rapide.
J'ai refait les tests en configuration Release, et https://github.com/groue/GRDB.swift donne des chiffres plus réalistes : en l'état actuel des choses, GRDB est 4 fois plus lent que fmdb à traiter des lignes.
Désormais les performances de GRDB sont meilleures que celles de SQLite.swift et FMDB : https://github.com/groue/GRDB.swift/wiki/Performance
Ca a l'air bien.
Est-ce que ça pourrait compiler sous Linux ?
Oui, je suis ce qui se passe sur ce framework depuis quelques temps et j'avais vu les différences. Intéressant.
Je pense passer à GRDB quand je passerai mon appli à Swift, dans quelques mois. J'attends Swift 3 et je vois que tu suis bien les évolutions de Swift.
Je ne travaille pas sur Linux, donc je n'ai pas fait l'effort de m'en assurer. De plus GRDB utilise Foundation pour quelques fondamentaux, comme NSData pour les blobs. Mais la qualité de Foundation sur linux est encore vacillante. En résumé : toutes les contributions sont bienvenues, mais il ne faut pas être trop pressé ni croire que tout va marcher comme sur des roulettes sans effort sérieux.
Tu fais exactement ce que tu veux : je n'ai pas écrit cette lib pour toi ni dans l'attente de ton jugement, mais parce que j'en ai besoin et que je veux fournir du code correct à moi-même et mes collègues.
Su tu n'attends pas un jugement des autres, je ne vois pas l'intérêt de la mettre en open source.
Oh mais que c'est bon esprit tout ça !
Comprenez-moi bien : lire qu'on daignera jeter un regard sur mon travail si "je suis bien les évolutions de Swift", ce n'est pas agréable.
Heu, je n'ai pas tout compris... Je te faisais justement un compliment en disant que tu suivais de près les évolutions de Swift. Et que quant à moi, j'attendais Swift 3 pour passer à Swift.
Ce n'est pas du tout le sens de ma phrase, m'enfin... ::)
... oups, je me suis complètement trompé sur le sens de ta phrase, tu as raison !
Toutes mes excuses, je t'ai cru à tord hautain et dédaigneux... Et désolé aussi pour la pollution du thread. ça m'apprendra à mal lire.
Hé bien du coup, merci, et j'espère que tu apprécieras GRDB si tu utilises SQLite quand Swift 3 sortira ! Va aussi voir https://medium.com/@gwendal.roue/grdb-stories-993b9d016867, c'est une suite de petites histoires courtes autour de GRDB qui montrent, j'espère, que la lib est là pour rendre service.
J'ai compris ce que tu voulais dire.
En un peu plus explicite, Swift, c'est des changements (1.0, 2.0, 2.2) et encore, je ne suis au courant que parce que je passe sur StackOverflow et que je vois nombre de questions " Comment faire pour passer mon code dans la version supérieure parce que maintenant ça ne compile plus ? Ouin ouin ", et j'ai compris que muqaddar te félicitait d'updater ton code aux dernières versions Swift.
D'avoir du code maintenu en bref.
Maintenant, si tu n'es intéressé que pour partager le code avec toi-même et quelques développeurs/collègues triés sur le volet, je te conseillerais peut-être d'avoir un repo de git privé.
Car un repo public, entraà®nera souvent des remontées de tickets de bugs, des propositions d'améliorations (que ce soit des pulls requests ou des gens qui voudront telle fonctionnalité mais ne voudront pas ou ne sauront pas la coder), etc. Et le SAV, à moins de l'accepter...
La " magie d'Internet " en quelque sorte.
Ah, je préfère ceci ! J'ai pensé à un "accident de lecture" aussi.
J'ai vu qu'il existe 4 ou 5 librairies SQLite Swift et la tienne me paraà®t la meilleure, et la plus complète.
J'irais lire les histoires sur Medium, mais celle qui me plaà®t le plus est celle sur la concurrence entre une action réseau et une action locale.
La façon dont tu écris les requêtes ressemble à ActiveRecord, donc ça me plaà®t en tant que "Rails addict". Cela dit, je ne suis pas sûr de l'utiliser, car écrire du pur SQL est un avantage en cas de portage multi-plateformes.
Sinon, j'utilise actuellement SQLCipher pour encrypter la base. C'est compatible avec FMDB, je ne sais pas si ça le serait avec GRDB. Mais à l'heure actuelle, je ne sais même pas si je vais le garder dans mon app de toute façon.
Là maintenant c'est bon esprit
@Larme: geÌneÌralement les gens ne se plaignent pas du fait que leur code ne fonctionne pas mais plutôt du fait qu'un code trouveÌ sur le net n'est pas compatible avec la version de Swift que tu as. Si tu fais du Swift de manieÌ€re sérieuse et journalière tu es censeÌ être au courant des changements. (enfin normalement)
Salut Larme. Tu as vu, c'était un quiproquo. Et tu verras que si GRDB n'a pas d'issue ouverte sur Github, ce n'est pas parce que la lib est confidentielle, mais parce que je réponds aux demandes de la communauté. Et tu verras peut-être aussi que j'ai mis en ligne GRMustache et GRMustache.swift, deux libs qui ont leur petit succès, et aussi mon attention quant aux questions des développeurs. ça fait quelques années que je maintiens du code et suis plongé dans la magie d'Internet !
Puisque tu la connais, cette magie, alors tu sais que la naiveté ne tient pas longtemps, parce que tu finis par être confronté à des gens vraiment désagréables, que tu n'as pas du tout envie d'aider. Et répondre sèchement aux gens fais partie, à tord ou à raison, de mon arsenal de réponses. Je suis désolé d'avoir cru que muqaddar était de ce type : c'était le quiproquo.
Bon, je suis soulagé !
GRDB a sa patte pour SQLCipher: https://github.com/groue/GRDB.swift#encryption, pour le jour ou tu voudras l'utiliser.
Moi pareil. J'ai ajouté la "query interface" pour que mon collègue qui n'est pas encore très à l'aise avec SQL ne se sente pas trop perdu.
Super, je n'avais pas vu. Merci.
Oui, ça permet de contrôler un peu mieux les jointures aussi (cas typique du includes() et du join() en Rails qui ne font pas la même chose au niveaux requêtes).
Déterrage de topic!
J'ai utilisé GRDB sur mon dernier projet. J'en suis franchement heureux. Je réitère à @groue les remerciements déjà faits sur Twitter.
- le code est super-propre
- la documentation est complète
- j'ai eu des soucis de performances avec SQLite, mais j'ai pu 1) utiliser les transactions, prévues par l'auteur et implémentées de façon élégante 2) désactiver certains contrôles de SQLite.
- je n'ai eu aucune galère. Les rares soucis découlaient d'incompréhensions de ma part, pas d'erreurs de conception.
- je suis très loin d'en avoir fait le tour, mais la courbe d'apprentissage est très raisonnable pour les opérations courantes.
Et en plus, elle évolue bien:
- passage à Swift 4 rapidement après la mise à disposition de Xcode 9.
- les Records peuvent utiliser le protocole Codable ce qui épargne du code inutile.
- il existe depuis peu une extension pour l'interfacer avec RxSwift! Si, si.
Le projet a l'air sympa, dommage qu'il n'y ai pas un exemple complet pour les 'Records'
Si justement, il n'y a qu'un seul exemple (un peu léger) mais qui démontre comment utiliser le FetchedRecordsController et aussi les Records.
Merci pour l'info, je vais regarder cela.