# LKDBHelper turns Objective-C models into SQLite tables without writing SQL

> LKDBHelper is an Objective-C ORM for SQLite on iOS, with automatic insert, query, update and delete against plain model objects, PRAGMA performance switches with published measurements, and a separate framework for recovering a corrupt database. The trade-offs are an iOS 12 floor, a dependency on FMDB, and a where clause API that quietly asks you to use strings for anything non-trivial.

**li6185377/LKDBHelper-SQLite-ORM** — 全自动的插入,查询,更新,删除， an automatic database operation  thread-safe and not afraid of recursive deadlock

- Repository: https://github.com/li6185377/LKDBHelper-SQLite-ORM
- Stars: 1,205 · Forks: 285
- Language: Objective-C
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/li6185377-lkdbhelper-sqlite-orm

## Automatic operations against plain model objects

LKDBHelper is described as a fully automatic SQLite ORM framework, with the first of its headline claims being thread safety and being unafraid of recursive deadlock, and the second being a claim of very high production daily active user numbers with excellent code stability, on the grounds that the shipping app depends on it through a pod dependency using the same GitHub code. The third is long term iteration, with the readme stating that systems from iOS 5 to iOS 26 work without problems.

Those are the claims of the project owner, and the audience is an iOS developer with an Objective-C codebase and a SQLite database that holds app state. The value proposition is that you declare a model class with properties and the library works out the table, the columns, the inserts and the queries.

The 2.0 release is what made that automatic in practice, with full support for automatic insert and query of properties typed as NSArray, NSDictionary, a model class, NSNumber, NSString, NSDate, NSData, UIColor, UIImage, CGRect, CGPoint, CGSize, NSRange, and the plain C types int, char, float, double and long. That list matters more than it looks, because those are exactly the types that a hand written ORM makes you convert by hand. A dictionary of mixed values, an array containing a nested model, an image, a colour: all of them can be stored and read back without a serialisation step of your own.

The requirements are iOS 12.0 or newer, ARC only, and FMDB as the underlying SQLite wrapper. The README notes that because of FMDB's own limits, anyone who needs to support systems before iOS 12 should pin themselves to FMDB 2.7.5 and LKDBHelper 2.6.3.

## The PRAGMA switches, and what the numbers actually show

The performance work is the most concrete part of the project, and it is measured rather than asserted.

A single switch, enablePerformanceOptimization, turns on a set of PRAGMA statements. mmap_size is set to 67108864 for a 64MB memory map, which reduces read and write system calls. cache_size is set to -8192 for an 8MB page cache, which reduces disk I/O. And temp_store is set to memory, which puts temporary tables and indexes in memory. WAL mode is a separate switch, enablePragmaWAL, because it is a different kind of decision.

The switches can be set on the helper instance, in a shared accessor:

```objective-c
+ (LKDBHelper *)getUsingLKDBHelper {
    static LKDBHelper *db;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        db = [[LKDBHelper alloc] init];
        db.enablePerformanceOptimization = YES;
    });
    return db;
}
```

Or globally, through a lifecycle callback. After LKDBHelper has finished initialising, before the database is opened, the onCreateWithLKDBHelper: class method on LKDBUtils is called, which is the place to put configuration that applies to the whole app:

```objective-c
+ (void)onCreateWithLKDBHelper:(LKDBHelper *)dbHelper {
    dbHelper.enablePerformanceOptimization = YES;
    dbHelper.enablePragmaWAL = YES;
}
```

The benchmark is described in enough detail to be judged: an iPhone device, iOS 26.2, SQLite 3.51.0, a model with more than twenty properties, and row by row operations with no transaction wrapping.

| Operation | Optimisation off | Optimisation on | Improvement |
| --- | --- | --- | --- |
| Insert 50 rows one by one | 9.1 ms | 3.6 ms | 60% |
| Full query 50 times | 1.9 ms | 1.5 ms | 21% |
| Random primary key lookups, 1000 times | 31.1 ms | 29.8 ms | 4% |
| Range queries, 200 times | 6.4 ms | 6.1 ms | 5% |
| Update 20 rows one by one | 0.9 ms | 0.8 ms | 11% |
| Total | 49.4 ms | 41.8 ms | 15% |

Read carefully, the shape of the result is that the win is concentrated in writes, where the improvement is 60 percent for inserts, and that lookups barely move at 4 and 5 percent. If your workload is read heavy, the PRAGMA switches will not save you, and if it is write heavy, they are a large and free win, since the measured gain on inserting 50 rows is a bit over five milliseconds for three lines of configuration.

## Models, table names, column mapping and the where clause

The basic usage is four steps. You create a model class with properties, optionally override the table name, optionally implement the callback methods, and then save objects.

A model is an ordinary Objective-C class:

```objective-c
@interface LKTest : NSObject
@property (nonatomic, copy) NSURL *url;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSUInteger age;
@property (nonatomic, assign) BOOL isGirl;

@property (nonatomic, strong) LKTestForeign *address;
@property (nonatomic, strong) NSArray *blah;
@property (nonatomic, strong) NSDictionary *hoho;
```

The table name is optional to override:

```objective-c
+ (NSString *)getTableName {
    return @"LKTestTable";
}
```

And the column mapping is optional too, which is where you rename a property, let a column inherit the default, or mark a property as one the library computes rather than stores:

```objective-c
+ (NSDictionary *)getTableMapping {
    return @{ @"name" : LKSQL_Mapping_Inherit,
              @"MyAge" : @"age",
              @"img" : LKSQL_Mapping_Inherit,
              @"MyDate" : @"date",
              @"address" : LKSQL_Mapping_UserCalculate,
              @"error" : LKSQL_Mapping_Inherit
              };
}
```

The query side accepts a where parameter that is either a dictionary or a string, and the README gives the forms: a single condition as a string such as rowid = 1 or a dictionary of rowid to 1, several conditions with and, an or condition, an in list such as rowid in (1,2,3) or a dictionary of rowid to an array, and a composite of both.

Then there is the caveat, and it is the one to internalise. When the where clause is an or type, such as rowid = 1 or sex = 0, you only use NSString. And if you want the condition to be judged, for example a date comparison like date >= '2013-04-01 00:00:00', only use NSString. In other words, the dictionary form is a convenience for simple equality and membership, and anything that needs real SQL semantics has to be written as a string, which is also the form that carries injection risk if you build it from user input.

## Callbacks, lifecycle hooks and a repair tool for corrupt files

Two extension mechanisms cover the cases where the automatic behaviour is not enough.

The first is the delegate category, an informal protocol implemented as class methods on NSObject: the table was created, the table was altered and which columns were added, and the will and did callbacks around insert, update and delete, plus a callback for when a read finishes. These are class methods rather than instance methods, which is the older Objective-C convention and means a model class can implement them without declaring conformance to anything.

The second is the recovery framework. When a database is damaged and you see the SQLITE_CORRUPT error or the message that the database disk image is malformed, the project ships LKDBRecover.xcframework to repair it, with the API documented in the header files. The principle is stated precisely: it is based on the recover API in the sqlite3.org source tree, with the framework based on version 3.49.1 dated 2025-02-18.

That last point is worth pausing on, because it is a real engineering decision rather than a feature list entry. A recovery tool that uses the same SQLite version as the database that produced the file has a better chance of understanding the file format, and building on the upstream recover implementation rather than a bespoke repair means the behaviour is the behaviour of SQLite's own tooling. The trade-off is that the framework carries its own SQLite version, which is the same class of issue as the FMDB version pinning, and it is a binary framework rather than source, so it is a black box that you import rather than inspect.

For an app shipping a local database, a corrupt file is an end of the user's data, and having a documented path to recover it, rather than deleting the file and losing everything, is worth having.

## Installing it, with FMDB and SQLCipher

The integration is CocoaPods, and the podspec is in the repository as LKDBHelper.podspec.json. The basic line is one entry:

```objective-c
pod 'LKDBHelper'
```

For a project that still supports systems before iOS 12, both dependencies are pinned explicitly, because the FMDB version has to match what the older LKDBHelper release expects:

```objective-c
pod 'LKDBHelper', '2.6.3'
pod 'FMDB', '2.7.5'
```

For encrypted storage there is a separate subspec, which pulls in FMDB's SQLCipher build and LKDBHelper's matching variant:

```objective-c
pod 'FMDB/SQLCipher'
pod 'LKDBHelper/FMDBSQLCipher'
```

With encryption on, the key is set on the database object, and the header documents the order: the setKey call is invoked after the LKDBHelper initialise in the getUsingLKDBHelper function of your model class file. There is also a rekey call to reset the key.

The repository also carries a Package.swift and a .swift-version file, so there is a Swift Package Manager route, and the demo directory shows three sample applications: Demo-iOS, Demo-MAC and Demo-Swift. The Swift demo is the interesting one to look at if you are Objective-C first and want to know how the library looks from Swift, since the library itself is Objective-C and the primary language of the repository is recorded as Objective-C.

The Migrations story is separate from the model mapping, and is a callback in the model class rather than a file on disk, which is a difference from the tools that generate migration files. The README lists a table structure update callback among the optional overrides, alongside the getTableName and getTableMapping methods, so a schema change is handled by the library reacting to the model rather than by you writing a migration.

## Where the project stands

The release history shows a project with a long life and a recent burst of maintenance work. The recent tags are 2.6.4 on 2024-11-04, described as a minor performance optimisation that keeps iOS 11 as the minimum, 2.7.2 on the same day, adapting to FMDB 2.7.12 with LKDB performance work, and 2.8.0 on 2025-12-16, combining performance optimisation, a database corruption fix and a SQLCipher reference correction.

The last push was on 2026-05-06 and the repository is not archived, so the work continues. The licence is MIT, with the LICENSE file in the repository root.

Two structural points to keep in mind when you weigh an adoption. The first is the dependency on FMDB, which is not vendored and which you have to add to your Podfile yourself, and whose version has to be matched to the LKDBHelper version, so an upgrade is a two step decision rather than one. The second is the Objective-C heritage. The library is written in Objective-C, which means it will work from Swift through the interop layer, but it also means the API you will use is the Objective-C one, with class methods on the model and selectors like searchWithWhere:orderBy:offset:count: rather than the async and throwing shapes a modern Swift client would use.

The project also maintains a Chinese blog and the README is primarily in Chinese, with the English sections covering installation, usage and mapping. The screenshots directory and the three demo projects are the fastest way to see what the automatic behaviour actually does with a nested model, an array of mixed values and a dictionary containing a date.

## Conclusion

LKDBHelper suits an iOS team with an existing Objective-C codebase that stores app state in SQLite and wants models to map to tables without hand written SQL, and it suits anyone who needs to read or repair a damaged SQLite file. It is the wrong choice for a new Swift-first project, since the library is Objective-C and the Swift demos sit alongside it rather than replacing it, and a poor match if you want complex queries, because the where parameter only supports strings for conditions that need judging, such as date comparisons. Before adopting, check your deployment target against the iOS 12 floor, add the FMDB pod explicitly, and decide whether you need the SQLCipher subspec for encrypted storage.

## FAQ

### What is LKDBHelper and what does it automate?

LKDBHelper is a fully automatic SQLite ORM framework for Objective-C on iOS. Since version 2.0 it supports automatic insert and query for properties typed as NSArray, NSDictionary, a model class, NSNumber, NSString, NSDate, NSData, UIColor, UIImage, CGRect, CGPoint, CGSize, NSRange and the plain C scalar types, on top of FMDB.

### How do I enable the LKDBHelper performance optimisations?

Set enablePerformanceOptimization to YES on the helper, or set it in the onCreateWithLKDBHelper: callback on LKDBUtils, which runs after initialisation and before the database is opened. That switch sets mmap_size to 64MB, cache_size to 8MB and temp_store to memory, and WAL is enabled separately with enablePragmaWAL.

### What does the LKDBHelper where parameter accept?

Either an NSDictionary or an NSString, with forms for a single condition, several conditions joined by and, an or condition, an in list and a composite. The documentation notes that when the condition is an or type, or when it needs to be judged such as a date comparison, you should use only NSString.

### Can LKDBHelper repair a corrupt SQLite database?

Yes. The project ships LKDBRecover.xcframework for the SQLITE_CORRUPT and database disk image is malformed errors, built on the recover API from the sqlite3.org source tree and based on SQLite 3.49.1 dated 2025-02-18.

## Sources

- [Issues](https://github.com/li6185377/LKDBHelper-SQLite-ORM/issues)
- [li6185377/LKDBHelper-SQLite-ORM on GitHub](https://github.com/li6185377/LKDBHelper-SQLite-ORM)
- [License: MIT](https://github.com/li6185377/LKDBHelper-SQLite-ORM/blob/master/LICENSE)
- [README](https://github.com/li6185377/LKDBHelper-SQLite-ORM/blob/master/README.md)
- [Releases](https://github.com/li6185377/LKDBHelper-SQLite-ORM/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/li6185377-lkdbhelper-sqlite-orm
