├── .gitignore ├── JKDBModel.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── JKDBModel.xccheckout │ └── xcuserdata │ │ ├── root.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ │ └── zx_04.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── root.xcuserdatad │ └── xcschemes │ │ ├── JKDBModel.xcscheme │ │ └── xcschememanagement.plist │ └── zx_04.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── JKDBModel.xcscheme │ └── xcschememanagement.plist ├── JKDBModel ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ └── LaunchScreen.xib ├── Controllers │ ├── Main.storyboard │ ├── QueryTableViewController.h │ ├── QueryTableViewController.m │ ├── ViewController.h │ └── ViewController.m ├── DBModel │ ├── JKDBHelper.h │ ├── JKDBHelper.m │ ├── JKDBModel.h │ └── JKDBModel.m ├── FMDB │ ├── FMDB.h │ ├── FMDatabase.h │ ├── FMDatabase.m │ ├── FMDatabaseAdditions.h │ ├── FMDatabaseAdditions.m │ ├── FMDatabasePool.h │ ├── FMDatabasePool.m │ ├── FMDatabaseQueue.h │ ├── FMDatabaseQueue.m │ ├── FMResultSet.h │ └── FMResultSet.m ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── eay.imageset │ │ ├── Contents.json │ │ └── eay@2x.png │ └── portrait.imageset │ │ ├── Contents.json │ │ └── portrait.png ├── Info.plist ├── Models │ ├── Depart.h │ ├── Depart.m │ ├── User.h │ └── User.m ├── README.md └── main.m ├── JKDBModelTests ├── Info.plist └── JKDBModelTests.m ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | ld/ 2 | *.pbxuser 3 | !default.pbxuser 4 | *.mode1v3 5 | !default.mode1v3 6 | *.mode2v3 7 | !default.mode2v3 8 | *.perspectivev3 9 | !default.perspectivev3 10 | xcuserdata 11 | *.xccheckout 12 | *.moved-aside 13 | DerivedData 14 | *.hmap 15 | *.ipa 16 | *.xcuserstate 17 | .DS_Store 18 | -------------------------------------------------------------------------------- /JKDBModel.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 610FA0101B43D36F00CA50C6 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 610FA00F1B43D36F00CA50C6 /* main.m */; }; 11 | 610FA0131B43D36F00CA50C6 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 610FA0121B43D36F00CA50C6 /* AppDelegate.m */; }; 12 | 610FA01B1B43D36F00CA50C6 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 610FA01A1B43D36F00CA50C6 /* Images.xcassets */; }; 13 | 610FA01E1B43D36F00CA50C6 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 610FA01C1B43D36F00CA50C6 /* LaunchScreen.xib */; }; 14 | 610FA02A1B43D36F00CA50C6 /* JKDBModelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 610FA0291B43D36F00CA50C6 /* JKDBModelTests.m */; }; 15 | 610FA0371B43D42E00CA50C6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 610FA0341B43D42E00CA50C6 /* Main.storyboard */; }; 16 | 610FA0381B43D42E00CA50C6 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 610FA0361B43D42E00CA50C6 /* ViewController.m */; }; 17 | 610FA03E1B43D43B00CA50C6 /* Depart.m in Sources */ = {isa = PBXBuildFile; fileRef = 610FA03B1B43D43B00CA50C6 /* Depart.m */; }; 18 | 610FA03F1B43D43B00CA50C6 /* User.m in Sources */ = {isa = PBXBuildFile; fileRef = 610FA03D1B43D43B00CA50C6 /* User.m */; }; 19 | 610FA0451B43D44300CA50C6 /* JKDBHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 610FA0421B43D44300CA50C6 /* JKDBHelper.m */; }; 20 | 610FA0461B43D44300CA50C6 /* JKDBModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 610FA0441B43D44300CA50C6 /* JKDBModel.m */; }; 21 | 610FA0531B43D44A00CA50C6 /* FMDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = 610FA0491B43D44A00CA50C6 /* FMDatabase.m */; }; 22 | 610FA0541B43D44A00CA50C6 /* FMDatabaseAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 610FA04B1B43D44A00CA50C6 /* FMDatabaseAdditions.m */; }; 23 | 610FA0551B43D44A00CA50C6 /* FMDatabasePool.m in Sources */ = {isa = PBXBuildFile; fileRef = 610FA04D1B43D44A00CA50C6 /* FMDatabasePool.m */; }; 24 | 610FA0561B43D44A00CA50C6 /* FMDatabaseQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 610FA04F1B43D44A00CA50C6 /* FMDatabaseQueue.m */; }; 25 | 610FA0571B43D44A00CA50C6 /* FMResultSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 610FA0521B43D44A00CA50C6 /* FMResultSet.m */; }; 26 | 610FA0591B43D47F00CA50C6 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 610FA0581B43D47F00CA50C6 /* libsqlite3.dylib */; }; 27 | 6157C3A51B5B3A1800592F3F /* QueryTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6157C3A41B5B3A1800592F3F /* QueryTableViewController.m */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXContainerItemProxy section */ 31 | 610FA0241B43D36F00CA50C6 /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = 610FA0021B43D36F00CA50C6 /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = 610FA0091B43D36F00CA50C6; 36 | remoteInfo = JKDBModel; 37 | }; 38 | /* End PBXContainerItemProxy section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | 610FA00A1B43D36F00CA50C6 /* JKDBModel.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JKDBModel.app; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 610FA00E1B43D36F00CA50C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 43 | 610FA00F1B43D36F00CA50C6 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 44 | 610FA0111B43D36F00CA50C6 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 45 | 610FA0121B43D36F00CA50C6 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 46 | 610FA01A1B43D36F00CA50C6 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 47 | 610FA01D1B43D36F00CA50C6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 48 | 610FA0231B43D36F00CA50C6 /* JKDBModelTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = JKDBModelTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 49 | 610FA0281B43D36F00CA50C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | 610FA0291B43D36F00CA50C6 /* JKDBModelTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JKDBModelTests.m; sourceTree = ""; }; 51 | 610FA0341B43D42E00CA50C6 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 52 | 610FA0351B43D42E00CA50C6 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 53 | 610FA0361B43D42E00CA50C6 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 54 | 610FA03A1B43D43B00CA50C6 /* Depart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Depart.h; sourceTree = ""; }; 55 | 610FA03B1B43D43B00CA50C6 /* Depart.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Depart.m; sourceTree = ""; }; 56 | 610FA03C1B43D43B00CA50C6 /* User.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = User.h; sourceTree = ""; }; 57 | 610FA03D1B43D43B00CA50C6 /* User.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = User.m; sourceTree = ""; }; 58 | 610FA0411B43D44300CA50C6 /* JKDBHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JKDBHelper.h; sourceTree = ""; }; 59 | 610FA0421B43D44300CA50C6 /* JKDBHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JKDBHelper.m; sourceTree = ""; }; 60 | 610FA0431B43D44300CA50C6 /* JKDBModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JKDBModel.h; sourceTree = ""; }; 61 | 610FA0441B43D44300CA50C6 /* JKDBModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JKDBModel.m; sourceTree = ""; }; 62 | 610FA0481B43D44A00CA50C6 /* FMDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabase.h; sourceTree = ""; }; 63 | 610FA0491B43D44A00CA50C6 /* FMDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabase.m; sourceTree = ""; }; 64 | 610FA04A1B43D44A00CA50C6 /* FMDatabaseAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabaseAdditions.h; sourceTree = ""; }; 65 | 610FA04B1B43D44A00CA50C6 /* FMDatabaseAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabaseAdditions.m; sourceTree = ""; }; 66 | 610FA04C1B43D44A00CA50C6 /* FMDatabasePool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabasePool.h; sourceTree = ""; }; 67 | 610FA04D1B43D44A00CA50C6 /* FMDatabasePool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabasePool.m; sourceTree = ""; }; 68 | 610FA04E1B43D44A00CA50C6 /* FMDatabaseQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabaseQueue.h; sourceTree = ""; }; 69 | 610FA04F1B43D44A00CA50C6 /* FMDatabaseQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabaseQueue.m; sourceTree = ""; }; 70 | 610FA0501B43D44A00CA50C6 /* FMDB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDB.h; sourceTree = ""; }; 71 | 610FA0511B43D44A00CA50C6 /* FMResultSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMResultSet.h; sourceTree = ""; }; 72 | 610FA0521B43D44A00CA50C6 /* FMResultSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMResultSet.m; sourceTree = ""; }; 73 | 610FA0581B43D47F00CA50C6 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; 74 | 6157C3A31B5B3A1800592F3F /* QueryTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QueryTableViewController.h; sourceTree = ""; }; 75 | 6157C3A41B5B3A1800592F3F /* QueryTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QueryTableViewController.m; sourceTree = ""; }; 76 | /* End PBXFileReference section */ 77 | 78 | /* Begin PBXFrameworksBuildPhase section */ 79 | 610FA0071B43D36F00CA50C6 /* Frameworks */ = { 80 | isa = PBXFrameworksBuildPhase; 81 | buildActionMask = 2147483647; 82 | files = ( 83 | 610FA0591B43D47F00CA50C6 /* libsqlite3.dylib in Frameworks */, 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | 610FA0201B43D36F00CA50C6 /* Frameworks */ = { 88 | isa = PBXFrameworksBuildPhase; 89 | buildActionMask = 2147483647; 90 | files = ( 91 | ); 92 | runOnlyForDeploymentPostprocessing = 0; 93 | }; 94 | /* End PBXFrameworksBuildPhase section */ 95 | 96 | /* Begin PBXGroup section */ 97 | 610FA0011B43D36F00CA50C6 = { 98 | isa = PBXGroup; 99 | children = ( 100 | 610FA0581B43D47F00CA50C6 /* libsqlite3.dylib */, 101 | 610FA00C1B43D36F00CA50C6 /* JKDBModel */, 102 | 610FA0261B43D36F00CA50C6 /* JKDBModelTests */, 103 | 610FA00B1B43D36F00CA50C6 /* Products */, 104 | ); 105 | sourceTree = ""; 106 | }; 107 | 610FA00B1B43D36F00CA50C6 /* Products */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 610FA00A1B43D36F00CA50C6 /* JKDBModel.app */, 111 | 610FA0231B43D36F00CA50C6 /* JKDBModelTests.xctest */, 112 | ); 113 | name = Products; 114 | sourceTree = ""; 115 | }; 116 | 610FA00C1B43D36F00CA50C6 /* JKDBModel */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 610FA0471B43D44A00CA50C6 /* FMDB */, 120 | 610FA0401B43D44300CA50C6 /* DBModel */, 121 | 610FA0391B43D43B00CA50C6 /* Models */, 122 | 610FA0331B43D42E00CA50C6 /* Controllers */, 123 | 610FA0111B43D36F00CA50C6 /* AppDelegate.h */, 124 | 610FA0121B43D36F00CA50C6 /* AppDelegate.m */, 125 | 610FA01A1B43D36F00CA50C6 /* Images.xcassets */, 126 | 610FA01C1B43D36F00CA50C6 /* LaunchScreen.xib */, 127 | 610FA00D1B43D36F00CA50C6 /* Supporting Files */, 128 | ); 129 | path = JKDBModel; 130 | sourceTree = ""; 131 | }; 132 | 610FA00D1B43D36F00CA50C6 /* Supporting Files */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | 610FA00E1B43D36F00CA50C6 /* Info.plist */, 136 | 610FA00F1B43D36F00CA50C6 /* main.m */, 137 | ); 138 | name = "Supporting Files"; 139 | sourceTree = ""; 140 | }; 141 | 610FA0261B43D36F00CA50C6 /* JKDBModelTests */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | 610FA0291B43D36F00CA50C6 /* JKDBModelTests.m */, 145 | 610FA0271B43D36F00CA50C6 /* Supporting Files */, 146 | ); 147 | path = JKDBModelTests; 148 | sourceTree = ""; 149 | }; 150 | 610FA0271B43D36F00CA50C6 /* Supporting Files */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | 610FA0281B43D36F00CA50C6 /* Info.plist */, 154 | ); 155 | name = "Supporting Files"; 156 | sourceTree = ""; 157 | }; 158 | 610FA0331B43D42E00CA50C6 /* Controllers */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | 6157C3A31B5B3A1800592F3F /* QueryTableViewController.h */, 162 | 6157C3A41B5B3A1800592F3F /* QueryTableViewController.m */, 163 | 610FA0341B43D42E00CA50C6 /* Main.storyboard */, 164 | 610FA0351B43D42E00CA50C6 /* ViewController.h */, 165 | 610FA0361B43D42E00CA50C6 /* ViewController.m */, 166 | ); 167 | path = Controllers; 168 | sourceTree = ""; 169 | }; 170 | 610FA0391B43D43B00CA50C6 /* Models */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | 610FA03A1B43D43B00CA50C6 /* Depart.h */, 174 | 610FA03B1B43D43B00CA50C6 /* Depart.m */, 175 | 610FA03C1B43D43B00CA50C6 /* User.h */, 176 | 610FA03D1B43D43B00CA50C6 /* User.m */, 177 | ); 178 | path = Models; 179 | sourceTree = ""; 180 | }; 181 | 610FA0401B43D44300CA50C6 /* DBModel */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | 610FA0411B43D44300CA50C6 /* JKDBHelper.h */, 185 | 610FA0421B43D44300CA50C6 /* JKDBHelper.m */, 186 | 610FA0431B43D44300CA50C6 /* JKDBModel.h */, 187 | 610FA0441B43D44300CA50C6 /* JKDBModel.m */, 188 | ); 189 | path = DBModel; 190 | sourceTree = ""; 191 | }; 192 | 610FA0471B43D44A00CA50C6 /* FMDB */ = { 193 | isa = PBXGroup; 194 | children = ( 195 | 610FA0481B43D44A00CA50C6 /* FMDatabase.h */, 196 | 610FA0491B43D44A00CA50C6 /* FMDatabase.m */, 197 | 610FA04A1B43D44A00CA50C6 /* FMDatabaseAdditions.h */, 198 | 610FA04B1B43D44A00CA50C6 /* FMDatabaseAdditions.m */, 199 | 610FA04C1B43D44A00CA50C6 /* FMDatabasePool.h */, 200 | 610FA04D1B43D44A00CA50C6 /* FMDatabasePool.m */, 201 | 610FA04E1B43D44A00CA50C6 /* FMDatabaseQueue.h */, 202 | 610FA04F1B43D44A00CA50C6 /* FMDatabaseQueue.m */, 203 | 610FA0501B43D44A00CA50C6 /* FMDB.h */, 204 | 610FA0511B43D44A00CA50C6 /* FMResultSet.h */, 205 | 610FA0521B43D44A00CA50C6 /* FMResultSet.m */, 206 | ); 207 | path = FMDB; 208 | sourceTree = ""; 209 | }; 210 | /* End PBXGroup section */ 211 | 212 | /* Begin PBXNativeTarget section */ 213 | 610FA0091B43D36F00CA50C6 /* JKDBModel */ = { 214 | isa = PBXNativeTarget; 215 | buildConfigurationList = 610FA02D1B43D36F00CA50C6 /* Build configuration list for PBXNativeTarget "JKDBModel" */; 216 | buildPhases = ( 217 | 610FA0061B43D36F00CA50C6 /* Sources */, 218 | 610FA0071B43D36F00CA50C6 /* Frameworks */, 219 | 610FA0081B43D36F00CA50C6 /* Resources */, 220 | ); 221 | buildRules = ( 222 | ); 223 | dependencies = ( 224 | ); 225 | name = JKDBModel; 226 | productName = JKDBModel; 227 | productReference = 610FA00A1B43D36F00CA50C6 /* JKDBModel.app */; 228 | productType = "com.apple.product-type.application"; 229 | }; 230 | 610FA0221B43D36F00CA50C6 /* JKDBModelTests */ = { 231 | isa = PBXNativeTarget; 232 | buildConfigurationList = 610FA0301B43D36F00CA50C6 /* Build configuration list for PBXNativeTarget "JKDBModelTests" */; 233 | buildPhases = ( 234 | 610FA01F1B43D36F00CA50C6 /* Sources */, 235 | 610FA0201B43D36F00CA50C6 /* Frameworks */, 236 | 610FA0211B43D36F00CA50C6 /* Resources */, 237 | ); 238 | buildRules = ( 239 | ); 240 | dependencies = ( 241 | 610FA0251B43D36F00CA50C6 /* PBXTargetDependency */, 242 | ); 243 | name = JKDBModelTests; 244 | productName = JKDBModelTests; 245 | productReference = 610FA0231B43D36F00CA50C6 /* JKDBModelTests.xctest */; 246 | productType = "com.apple.product-type.bundle.unit-test"; 247 | }; 248 | /* End PBXNativeTarget section */ 249 | 250 | /* Begin PBXProject section */ 251 | 610FA0021B43D36F00CA50C6 /* Project object */ = { 252 | isa = PBXProject; 253 | attributes = { 254 | LastUpgradeCheck = 0620; 255 | ORGANIZATIONNAME = Mac; 256 | TargetAttributes = { 257 | 610FA0091B43D36F00CA50C6 = { 258 | CreatedOnToolsVersion = 6.2; 259 | }; 260 | 610FA0221B43D36F00CA50C6 = { 261 | CreatedOnToolsVersion = 6.2; 262 | TestTargetID = 610FA0091B43D36F00CA50C6; 263 | }; 264 | }; 265 | }; 266 | buildConfigurationList = 610FA0051B43D36F00CA50C6 /* Build configuration list for PBXProject "JKDBModel" */; 267 | compatibilityVersion = "Xcode 3.2"; 268 | developmentRegion = English; 269 | hasScannedForEncodings = 0; 270 | knownRegions = ( 271 | en, 272 | Base, 273 | ); 274 | mainGroup = 610FA0011B43D36F00CA50C6; 275 | productRefGroup = 610FA00B1B43D36F00CA50C6 /* Products */; 276 | projectDirPath = ""; 277 | projectRoot = ""; 278 | targets = ( 279 | 610FA0091B43D36F00CA50C6 /* JKDBModel */, 280 | 610FA0221B43D36F00CA50C6 /* JKDBModelTests */, 281 | ); 282 | }; 283 | /* End PBXProject section */ 284 | 285 | /* Begin PBXResourcesBuildPhase section */ 286 | 610FA0081B43D36F00CA50C6 /* Resources */ = { 287 | isa = PBXResourcesBuildPhase; 288 | buildActionMask = 2147483647; 289 | files = ( 290 | 610FA01E1B43D36F00CA50C6 /* LaunchScreen.xib in Resources */, 291 | 610FA01B1B43D36F00CA50C6 /* Images.xcassets in Resources */, 292 | 610FA0371B43D42E00CA50C6 /* Main.storyboard in Resources */, 293 | ); 294 | runOnlyForDeploymentPostprocessing = 0; 295 | }; 296 | 610FA0211B43D36F00CA50C6 /* Resources */ = { 297 | isa = PBXResourcesBuildPhase; 298 | buildActionMask = 2147483647; 299 | files = ( 300 | ); 301 | runOnlyForDeploymentPostprocessing = 0; 302 | }; 303 | /* End PBXResourcesBuildPhase section */ 304 | 305 | /* Begin PBXSourcesBuildPhase section */ 306 | 610FA0061B43D36F00CA50C6 /* Sources */ = { 307 | isa = PBXSourcesBuildPhase; 308 | buildActionMask = 2147483647; 309 | files = ( 310 | 610FA0131B43D36F00CA50C6 /* AppDelegate.m in Sources */, 311 | 610FA0461B43D44300CA50C6 /* JKDBModel.m in Sources */, 312 | 610FA0531B43D44A00CA50C6 /* FMDatabase.m in Sources */, 313 | 610FA03F1B43D43B00CA50C6 /* User.m in Sources */, 314 | 6157C3A51B5B3A1800592F3F /* QueryTableViewController.m in Sources */, 315 | 610FA0561B43D44A00CA50C6 /* FMDatabaseQueue.m in Sources */, 316 | 610FA0571B43D44A00CA50C6 /* FMResultSet.m in Sources */, 317 | 610FA0551B43D44A00CA50C6 /* FMDatabasePool.m in Sources */, 318 | 610FA0381B43D42E00CA50C6 /* ViewController.m in Sources */, 319 | 610FA03E1B43D43B00CA50C6 /* Depart.m in Sources */, 320 | 610FA0101B43D36F00CA50C6 /* main.m in Sources */, 321 | 610FA0451B43D44300CA50C6 /* JKDBHelper.m in Sources */, 322 | 610FA0541B43D44A00CA50C6 /* FMDatabaseAdditions.m in Sources */, 323 | ); 324 | runOnlyForDeploymentPostprocessing = 0; 325 | }; 326 | 610FA01F1B43D36F00CA50C6 /* Sources */ = { 327 | isa = PBXSourcesBuildPhase; 328 | buildActionMask = 2147483647; 329 | files = ( 330 | 610FA02A1B43D36F00CA50C6 /* JKDBModelTests.m in Sources */, 331 | ); 332 | runOnlyForDeploymentPostprocessing = 0; 333 | }; 334 | /* End PBXSourcesBuildPhase section */ 335 | 336 | /* Begin PBXTargetDependency section */ 337 | 610FA0251B43D36F00CA50C6 /* PBXTargetDependency */ = { 338 | isa = PBXTargetDependency; 339 | target = 610FA0091B43D36F00CA50C6 /* JKDBModel */; 340 | targetProxy = 610FA0241B43D36F00CA50C6 /* PBXContainerItemProxy */; 341 | }; 342 | /* End PBXTargetDependency section */ 343 | 344 | /* Begin PBXVariantGroup section */ 345 | 610FA01C1B43D36F00CA50C6 /* LaunchScreen.xib */ = { 346 | isa = PBXVariantGroup; 347 | children = ( 348 | 610FA01D1B43D36F00CA50C6 /* Base */, 349 | ); 350 | name = LaunchScreen.xib; 351 | sourceTree = ""; 352 | }; 353 | /* End PBXVariantGroup section */ 354 | 355 | /* Begin XCBuildConfiguration section */ 356 | 610FA02B1B43D36F00CA50C6 /* Debug */ = { 357 | isa = XCBuildConfiguration; 358 | buildSettings = { 359 | ALWAYS_SEARCH_USER_PATHS = NO; 360 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 361 | CLANG_CXX_LIBRARY = "libc++"; 362 | CLANG_ENABLE_MODULES = YES; 363 | CLANG_ENABLE_OBJC_ARC = YES; 364 | CLANG_WARN_BOOL_CONVERSION = YES; 365 | CLANG_WARN_CONSTANT_CONVERSION = YES; 366 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 367 | CLANG_WARN_EMPTY_BODY = YES; 368 | CLANG_WARN_ENUM_CONVERSION = YES; 369 | CLANG_WARN_INT_CONVERSION = YES; 370 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 371 | CLANG_WARN_UNREACHABLE_CODE = YES; 372 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 373 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 374 | COPY_PHASE_STRIP = NO; 375 | ENABLE_STRICT_OBJC_MSGSEND = YES; 376 | GCC_C_LANGUAGE_STANDARD = gnu99; 377 | GCC_DYNAMIC_NO_PIC = NO; 378 | GCC_OPTIMIZATION_LEVEL = 0; 379 | GCC_PREPROCESSOR_DEFINITIONS = ( 380 | "DEBUG=1", 381 | "$(inherited)", 382 | ); 383 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 384 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 385 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 386 | GCC_WARN_UNDECLARED_SELECTOR = YES; 387 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 388 | GCC_WARN_UNUSED_FUNCTION = YES; 389 | GCC_WARN_UNUSED_VARIABLE = YES; 390 | IPHONEOS_DEPLOYMENT_TARGET = 8.2; 391 | MTL_ENABLE_DEBUG_INFO = YES; 392 | ONLY_ACTIVE_ARCH = YES; 393 | SDKROOT = iphoneos; 394 | }; 395 | name = Debug; 396 | }; 397 | 610FA02C1B43D36F00CA50C6 /* Release */ = { 398 | isa = XCBuildConfiguration; 399 | buildSettings = { 400 | ALWAYS_SEARCH_USER_PATHS = NO; 401 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 402 | CLANG_CXX_LIBRARY = "libc++"; 403 | CLANG_ENABLE_MODULES = YES; 404 | CLANG_ENABLE_OBJC_ARC = YES; 405 | CLANG_WARN_BOOL_CONVERSION = YES; 406 | CLANG_WARN_CONSTANT_CONVERSION = YES; 407 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 408 | CLANG_WARN_EMPTY_BODY = YES; 409 | CLANG_WARN_ENUM_CONVERSION = YES; 410 | CLANG_WARN_INT_CONVERSION = YES; 411 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 412 | CLANG_WARN_UNREACHABLE_CODE = YES; 413 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 414 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 415 | COPY_PHASE_STRIP = NO; 416 | ENABLE_NS_ASSERTIONS = NO; 417 | ENABLE_STRICT_OBJC_MSGSEND = YES; 418 | GCC_C_LANGUAGE_STANDARD = gnu99; 419 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 420 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 421 | GCC_WARN_UNDECLARED_SELECTOR = YES; 422 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 423 | GCC_WARN_UNUSED_FUNCTION = YES; 424 | GCC_WARN_UNUSED_VARIABLE = YES; 425 | IPHONEOS_DEPLOYMENT_TARGET = 8.2; 426 | MTL_ENABLE_DEBUG_INFO = NO; 427 | SDKROOT = iphoneos; 428 | VALIDATE_PRODUCT = YES; 429 | }; 430 | name = Release; 431 | }; 432 | 610FA02E1B43D36F00CA50C6 /* Debug */ = { 433 | isa = XCBuildConfiguration; 434 | buildSettings = { 435 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 436 | INFOPLIST_FILE = JKDBModel/Info.plist; 437 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 438 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 439 | PRODUCT_NAME = "$(TARGET_NAME)"; 440 | }; 441 | name = Debug; 442 | }; 443 | 610FA02F1B43D36F00CA50C6 /* Release */ = { 444 | isa = XCBuildConfiguration; 445 | buildSettings = { 446 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 447 | INFOPLIST_FILE = JKDBModel/Info.plist; 448 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 449 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 450 | PRODUCT_NAME = "$(TARGET_NAME)"; 451 | }; 452 | name = Release; 453 | }; 454 | 610FA0311B43D36F00CA50C6 /* Debug */ = { 455 | isa = XCBuildConfiguration; 456 | buildSettings = { 457 | BUNDLE_LOADER = "$(TEST_HOST)"; 458 | FRAMEWORK_SEARCH_PATHS = ( 459 | "$(SDKROOT)/Developer/Library/Frameworks", 460 | "$(inherited)", 461 | ); 462 | GCC_PREPROCESSOR_DEFINITIONS = ( 463 | "DEBUG=1", 464 | "$(inherited)", 465 | ); 466 | INFOPLIST_FILE = JKDBModelTests/Info.plist; 467 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 468 | PRODUCT_NAME = "$(TARGET_NAME)"; 469 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/JKDBModel.app/JKDBModel"; 470 | }; 471 | name = Debug; 472 | }; 473 | 610FA0321B43D36F00CA50C6 /* Release */ = { 474 | isa = XCBuildConfiguration; 475 | buildSettings = { 476 | BUNDLE_LOADER = "$(TEST_HOST)"; 477 | FRAMEWORK_SEARCH_PATHS = ( 478 | "$(SDKROOT)/Developer/Library/Frameworks", 479 | "$(inherited)", 480 | ); 481 | INFOPLIST_FILE = JKDBModelTests/Info.plist; 482 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 483 | PRODUCT_NAME = "$(TARGET_NAME)"; 484 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/JKDBModel.app/JKDBModel"; 485 | }; 486 | name = Release; 487 | }; 488 | /* End XCBuildConfiguration section */ 489 | 490 | /* Begin XCConfigurationList section */ 491 | 610FA0051B43D36F00CA50C6 /* Build configuration list for PBXProject "JKDBModel" */ = { 492 | isa = XCConfigurationList; 493 | buildConfigurations = ( 494 | 610FA02B1B43D36F00CA50C6 /* Debug */, 495 | 610FA02C1B43D36F00CA50C6 /* Release */, 496 | ); 497 | defaultConfigurationIsVisible = 0; 498 | defaultConfigurationName = Release; 499 | }; 500 | 610FA02D1B43D36F00CA50C6 /* Build configuration list for PBXNativeTarget "JKDBModel" */ = { 501 | isa = XCConfigurationList; 502 | buildConfigurations = ( 503 | 610FA02E1B43D36F00CA50C6 /* Debug */, 504 | 610FA02F1B43D36F00CA50C6 /* Release */, 505 | ); 506 | defaultConfigurationIsVisible = 0; 507 | defaultConfigurationName = Release; 508 | }; 509 | 610FA0301B43D36F00CA50C6 /* Build configuration list for PBXNativeTarget "JKDBModelTests" */ = { 510 | isa = XCConfigurationList; 511 | buildConfigurations = ( 512 | 610FA0311B43D36F00CA50C6 /* Debug */, 513 | 610FA0321B43D36F00CA50C6 /* Release */, 514 | ); 515 | defaultConfigurationIsVisible = 0; 516 | defaultConfigurationName = Release; 517 | }; 518 | /* End XCConfigurationList section */ 519 | }; 520 | rootObject = 610FA0021B43D36F00CA50C6 /* Project object */; 521 | } 522 | -------------------------------------------------------------------------------- /JKDBModel.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /JKDBModel.xcodeproj/project.xcworkspace/xcshareddata/JKDBModel.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 53222715-4330-4C42-AE9D-0DBF87E483F0 9 | IDESourceControlProjectName 10 | JKDBModel 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | A749CFB0C1E9DC358FA1E68F6AF77D7A752581CD 14 | https://github.com/Joker-King/JKDBModel.git 15 | 16 | IDESourceControlProjectPath 17 | JKDBModel.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | A749CFB0C1E9DC358FA1E68F6AF77D7A752581CD 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/Joker-King/JKDBModel.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | A749CFB0C1E9DC358FA1E68F6AF77D7A752581CD 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | A749CFB0C1E9DC358FA1E68F6AF77D7A752581CD 36 | IDESourceControlWCCName 37 | JKDBModel 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /JKDBModel.xcodeproj/project.xcworkspace/xcuserdata/root.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xhellohaley/JKDBModel/470aaf8b114164774d5409a841eec4d2cfb061d1/JKDBModel.xcodeproj/project.xcworkspace/xcuserdata/root.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /JKDBModel.xcodeproj/project.xcworkspace/xcuserdata/zx_04.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xhellohaley/JKDBModel/470aaf8b114164774d5409a841eec4d2cfb061d1/JKDBModel.xcodeproj/project.xcworkspace/xcuserdata/zx_04.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /JKDBModel.xcodeproj/xcuserdata/root.xcuserdatad/xcschemes/JKDBModel.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 94 | 96 | 102 | 103 | 104 | 105 | 107 | 108 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /JKDBModel.xcodeproj/xcuserdata/root.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | JKDBModel.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 610FA0091B43D36F00CA50C6 16 | 17 | primary 18 | 19 | 20 | 610FA0221B43D36F00CA50C6 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /JKDBModel.xcodeproj/xcuserdata/zx_04.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /JKDBModel.xcodeproj/xcuserdata/zx_04.xcuserdatad/xcschemes/JKDBModel.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 94 | 96 | 102 | 103 | 104 | 105 | 107 | 108 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /JKDBModel.xcodeproj/xcuserdata/zx_04.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | JKDBModel.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 610FA0091B43D36F00CA50C6 16 | 17 | primary 18 | 19 | 20 | 610FA0221B43D36F00CA50C6 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /JKDBModel/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // JKDBModel 4 | // 5 | // Created by zx_04 on 15/7/1. 6 | // Copyright (c) 2015年 joker. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /JKDBModel/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // JKDBModel 4 | // 5 | // Created by zx_04 on 15/7/1. 6 | // Copyright (c) 2015年 joker. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /JKDBModel/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /JKDBModel/Controllers/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 52 | 63 | 74 | 85 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 109 | 120 | 127 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 155 | 162 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 190 | 201 | 212 | 219 | 230 | 231 | 232 | 233 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 276 | 283 | 290 | 297 | 304 | 311 | 318 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | -------------------------------------------------------------------------------- /JKDBModel/Controllers/QueryTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // QueryTableViewController.h 3 | // JKDBModel 4 | // 5 | // Created by zx_04 on 15/7/3. 6 | // Copyright (c) 2015年 joker. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface QueryTableViewController : UITableViewController 12 | 13 | @property (nonatomic, assign) int type; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /JKDBModel/Controllers/QueryTableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // QueryTableViewController.m 3 | // JKDBModel 4 | // 5 | // Created by zx_04 on 15/7/3. 6 | // Copyright (c) 2015年 joker. All rights reserved. 7 | // 8 | 9 | #import "QueryTableViewController.h" 10 | #import "User.h" 11 | 12 | @interface QueryTableViewController () 13 | 14 | @property (nonatomic, strong) NSMutableArray *data; 15 | 16 | @end 17 | 18 | @implementation QueryTableViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | 23 | self.tableView.tableFooterView = [UIView new]; 24 | if (_type == 4) { 25 | UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 30, 40)]; 26 | [button setTitle:@"点击加载更多" forState:UIControlStateNormal]; 27 | [button setTitleColor:[UIColor grayColor] forState:UIControlStateNormal]; 28 | [button addTarget:self action:@selector(loadMore) forControlEvents:UIControlEventTouchUpInside]; 29 | self.tableView.tableFooterView = button; 30 | } 31 | 32 | _data = [[NSMutableArray alloc] init]; 33 | [self loadData]; 34 | } 35 | 36 | - (void)loadData 37 | { 38 | switch (_type) { 39 | case 1: 40 | { 41 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 42 | NSLog(@"第一条"); 43 | // User *user = [User findFirstByCriteria:@" WHERE age = 20 "]; 44 | User *user = [User findFirstWithFormat:@" WHERE %@ = %d ",@"age",10]; 45 | if (!user) return; 46 | [self.data addObject:user]; 47 | [self.tableView reloadData]; 48 | }); 49 | break; 50 | } 51 | case 2: 52 | { 53 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 54 | NSLog(@"小于20岁"); 55 | // [self.data addObjectsFromArray:[User findByCriteria:@" WHERE age < 20 "]]; 56 | [self.data addObjectsFromArray:[User findWithFormat:@" WHERE %@ < %d",@"age",20]]; 57 | [self.tableView reloadData]; 58 | }); 59 | break; 60 | } 61 | case 3: 62 | { 63 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 64 | NSLog(@"全部"); 65 | [self.data addObjectsFromArray:[User findAll]]; 66 | [self.tableView reloadData]; 67 | }); 68 | break; 69 | } 70 | case 4: 71 | { 72 | [self loadMore]; 73 | } 74 | break; 75 | 76 | default: 77 | break; 78 | } 79 | 80 | } 81 | 82 | - (void)loadMore 83 | { 84 | static int pk = 5; 85 | NSArray *array = [User findByCriteria:[NSString stringWithFormat:@" WHERE pk > %d limit 10",pk]]; 86 | pk = ((User *)[array lastObject]).pk; 87 | [self.data addObjectsFromArray:array]; 88 | [self.tableView reloadData]; 89 | } 90 | 91 | #pragma mark - Table view data source 92 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 93 | return self.data.count; 94 | } 95 | 96 | 97 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 98 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"queryCell"]; 99 | 100 | UILabel *pkLabel = (UILabel *)[cell viewWithTag:101]; 101 | UILabel *nameLabel = (UILabel *)[cell viewWithTag:102]; 102 | UILabel *sexLabel = (UILabel *)[cell viewWithTag:103]; 103 | UILabel *ageLabel = (UILabel *)[cell viewWithTag:104]; 104 | UIImageView *imageView = (UIImageView *)[cell viewWithTag:105]; 105 | 106 | User *user = [self.data objectAtIndex:indexPath.row]; 107 | pkLabel.text = [NSString stringWithFormat:@"%d",user.pk]; 108 | nameLabel.text = user.name; 109 | sexLabel.text = user.sex; 110 | ageLabel.text = [NSString stringWithFormat:@"%d",user.age]; 111 | imageView.image = [UIImage imageWithData:user.imageData]; 112 | 113 | return cell; 114 | } 115 | 116 | @end 117 | -------------------------------------------------------------------------------- /JKDBModel/Controllers/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // JKBaseModel 4 | // 5 | // Created by zx_04 on 15/6/16. 6 | // Copyright (c) 2015年 joker. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /JKDBModel/Controllers/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // JKBaseModel 4 | // 5 | // Created by zx_04 on 15/6/16. 6 | // Copyright (c) 2015年 joker. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "QueryTableViewController.h" 11 | #import "User.h" 12 | #import "Depart.h" 13 | #import "JKDBHelper.h" 14 | 15 | @implementation ViewController 16 | 17 | #pragma mark - 插入数据 18 | /** 创建多条子线程 */ 19 | - (IBAction)insertData:(id)sender { 20 | UIImage *image = [UIImage imageNamed:@"portrait"]; 21 | NSData *imageData = UIImagePNGRepresentation(image); 22 | for (int i = 0; i < 10; i++) { 23 | User *user = [[User alloc] init]; 24 | user.name = [NSString stringWithFormat:@"麻子%d",i]; 25 | user.sex = @"男"; 26 | user.age = 10+i; 27 | user.createTime = 1368082020; 28 | user.imageData = imageData; 29 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 30 | [user save]; 31 | }); 32 | } 33 | } 34 | 35 | /** 子线程一:插入多条用户数据 */ 36 | - (IBAction)insertData2:(id)sender { 37 | 38 | dispatch_queue_t q1 = dispatch_queue_create("queue1", NULL); 39 | dispatch_async(q1, ^{ 40 | for (int i = 0; i < 5; ++i) { 41 | User *user = [[User alloc] init]; 42 | user.name = @"赵五"; 43 | user.sex = @"女"; 44 | user.age = i+5; 45 | [user save]; 46 | } 47 | }); 48 | } 49 | 50 | - (IBAction)insertData3:(id)sender { 51 | for (int i = 0; i < 1000; ++i) { 52 | User *user = [[User alloc] init]; 53 | user.name = @"张三"; 54 | user.sex = @"男"; 55 | user.age = i+5; 56 | [user save]; 57 | } 58 | } 59 | 60 | /** 子线程三:事务插入数据 */ 61 | - (IBAction)insertData4:(id)sender { 62 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 63 | NSMutableArray *array = [NSMutableArray array]; 64 | for (int i = 0; i < 500; i++) { 65 | User *user = [[User alloc] init]; 66 | user.name = [NSString stringWithFormat:@"李四%d",i]; 67 | user.age = 10+i; 68 | user.sex = @"女"; 69 | [array addObject:user]; 70 | } 71 | [User saveObjects:array]; 72 | }); 73 | } 74 | 75 | #pragma mark - 删除数据 76 | /** 通过条件删除数据 */ 77 | - (IBAction)deleteData:(id)sender { 78 | // [User deleteObjectsByCriteria:@" WHERE pk < 10"]; 79 | [User deleteObjectsWithFormat:@"Where %@ < %d",@"pk",10]; 80 | } 81 | 82 | /** 创建多个线程删除数据 */ 83 | - (IBAction)deleteData2:(id)sender { 84 | for (int i = 0; i < 5; i++) { 85 | User *user = [[User alloc] init]; 86 | user.pk = 1+i; 87 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 88 | [user deleteObject]; 89 | }); 90 | } 91 | } 92 | 93 | /** 子线程用事务删除数据 */ 94 | - (IBAction)deleteData3:(id)sender { 95 | 96 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 97 | NSMutableArray *array = [NSMutableArray array]; 98 | for (int i = 0; i < 500; i++) { 99 | User *user = [[User alloc] init]; 100 | user.pk = 501+i; 101 | [array addObject:user]; 102 | } 103 | [User deleteObjects:array]; 104 | }); 105 | } 106 | 107 | #pragma mark - 修改数据 108 | /** 创建多个线程更新数据 */ 109 | - (IBAction)updateData1:(id)sender { 110 | UIImage *image = [UIImage imageNamed:@"eay"]; 111 | NSData *imageData = UIImagePNGRepresentation(image); 112 | for (int i = 0; i < 5; i++) { 113 | User *user = [[User alloc] init]; 114 | user.name = [NSString stringWithFormat:@"更新%d",i]; 115 | user.age = 120+i; 116 | user.pk = 5+i; 117 | user.imageData = imageData; 118 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 119 | [user update]; 120 | }); 121 | } 122 | } 123 | 124 | /**单个子线程批量更新数据,利用事务 */ 125 | - (IBAction)updateData:(id)sender { 126 | dispatch_queue_t q3 = dispatch_queue_create("queue3", NULL); 127 | dispatch_async(q3, ^{ 128 | NSMutableArray *array = [NSMutableArray array]; 129 | for (int i = 0; i < 500; i++) { 130 | User *user = [[User alloc] init]; 131 | user.name = [NSString stringWithFormat:@"啊我哦%d",i]; 132 | user.age = 88+i; 133 | user.pk = 10+i; 134 | [array addObject:user]; 135 | } 136 | [User updateObjects:array]; 137 | }); 138 | 139 | } 140 | 141 | #pragma mark - 查询 142 | /** 查询单条记录 */ 143 | - (IBAction)queryData1:(id)sender { 144 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 145 | NSLog(@"第一条:%@",[User findFirstByCriteria:@" WHERE age = 20 "]); 146 | }); 147 | } 148 | 149 | /** 条件查询多条记录 */ 150 | - (IBAction)queryData2:(id)sender { 151 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 152 | NSLog(@"小于20岁:%@",[User findByCriteria:@" WHERE age < 20 "]); 153 | }); 154 | } 155 | 156 | /** 查询全部数据 */ 157 | - (IBAction)queryData3:(id)sender { 158 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 159 | NSLog(@"全部:%@",[User findAll]); 160 | }); 161 | } 162 | 163 | /** 分页查询数据 */ 164 | - (IBAction)queryData:(id)sender { 165 | static int pk = 5; 166 | NSArray *array = [User findByCriteria:[NSString stringWithFormat:@" WHERE pk > %d limit 10",pk]]; 167 | pk = ((User *)[array lastObject]).pk; 168 | NSLog(@"array:%@",array); 169 | } 170 | 171 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 172 | { 173 | NSString *title = @"查询"; 174 | int type = 3; 175 | if ([segue.identifier isEqualToString:@"One"]) { 176 | title = @"查询一条数据"; 177 | type = 1; 178 | } else if ([segue.identifier isEqualToString:@"Two"]){ 179 | title = @"条件查询"; 180 | type = 2; 181 | }else if ([segue.identifier isEqualToString:@"Three"]){ 182 | title = @"查询全部"; 183 | type = 3; 184 | }else if ([segue.identifier isEqualToString:@"Four"]){ 185 | title = @"分页查询"; 186 | type = 4; 187 | } 188 | 189 | QueryTableViewController *destVC = segue.destinationViewController; 190 | destVC.title = title; 191 | destVC.type = type; 192 | } 193 | 194 | - (IBAction)changeDire:(id)sender { 195 | [[JKDBHelper shareInstance] changeDBWithDirectoryName:@"Joker"]; 196 | } 197 | 198 | //#pragma mark - 不使用FMDatabaseQueue,直接使用FMDatabase 199 | // 200 | //- (NSString *)getDBPath 201 | //{ 202 | // NSString* docsdir = [NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; 203 | // NSFileManager *filemanage = [NSFileManager defaultManager]; 204 | // docsdir = [docsdir stringByAppendingPathComponent:@"FMDBDemo"]; 205 | // BOOL isDir; 206 | // BOOL exit =[filemanage fileExistsAtPath:docsdir isDirectory:&isDir]; 207 | // if (!exit || !isDir) { 208 | // [filemanage createDirectoryAtPath:docsdir withIntermediateDirectories:YES attributes:nil error:nil]; 209 | // } 210 | // NSString *dbpath = [docsdir stringByAppendingPathComponent:@"myDB.sqlite"]; 211 | // return dbpath; 212 | //} 213 | // 214 | ///** 用db创建User表*/ 215 | //- (IBAction)dbCreateUserTable:(id)sender { 216 | // [User createUserTableByDB]; 217 | //} 218 | // 219 | ///** 用db插入User数据*/ 220 | //- (IBAction)dbInsertData:(id)sender { 221 | // 222 | // //多线程插入数据 223 | // for (int i = 0; i < 5; i++) { 224 | // dispatch_async(dispatch_get_global_queue(0, 0), ^{ 225 | // User *user = [[User alloc] init]; 226 | // user.name = @"dbName一"; 227 | // user.ID_no = [NSString stringWithFormat:@"%d",55555+i]; 228 | // user.age = 555+i; 229 | // [user saveByDB]; 230 | // }); 231 | // } 232 | // 233 | // //利用事务插入数据 234 | // dispatch_async(dispatch_get_global_queue(0, 0), ^{ 235 | // NSMutableArray *array = [NSMutableArray array]; 236 | // for (int i = 0; i < 5; i++) { 237 | // User *user = [[User alloc] init]; 238 | // user.name = @"db事务"; 239 | // user.ID_no = [NSString stringWithFormat:@"%d",66666+i]; 240 | // user.age = 66+i; 241 | // [array addObject:user]; 242 | // [user release]; 243 | // } 244 | // 245 | // [User saveObjectsByDB:array]; 246 | // }); 247 | //} 248 | // 249 | ///** 用db删除User数据*/ 250 | //- (IBAction)dbDeleteData:(id)sender { 251 | // //多线程插入数据 252 | // for (int i = 0; i < 5; i++) { 253 | // dispatch_async(dispatch_get_global_queue(0, 0), ^{ 254 | // User *user = [[User alloc] init]; 255 | // user.ID = i+5; 256 | // [user deleteObjectByDB]; 257 | // }); 258 | // } 259 | // 260 | // //利用事务删除数据 261 | // dispatch_async(dispatch_get_global_queue(0, 0), ^{ 262 | // NSMutableArray *array = [NSMutableArray array]; 263 | // for (int i = 0; i < 5; i++) { 264 | // User *user = [[User alloc] init]; 265 | // user.ID = i+10; 266 | // [array addObject:user]; 267 | // [user release]; 268 | // } 269 | // 270 | // [User deleteObjectsByDB:array]; 271 | // }); 272 | //} 273 | // 274 | ///** 用db更新User数据*/ 275 | //- (IBAction)dbUpdateData:(id)sender { 276 | // //多线程插入数据 277 | // for (int i = 0; i < 5; i++) { 278 | // dispatch_async(dispatch_get_global_queue(0, 0), ^{ 279 | // 280 | // User *user = [[User alloc] init]; 281 | // user.name = @"db更新"; 282 | // user.ID_no = [NSString stringWithFormat:@"%d",55+i]; 283 | // user.age = 55555+i; 284 | // user.ID = i +1; 285 | // [user saveByDB]; 286 | // }); 287 | // } 288 | // 289 | // //利用事务更新数据 290 | // dispatch_async(dispatch_get_global_queue(0, 0), ^{ 291 | // NSMutableArray *array = [NSMutableArray array]; 292 | // for (int i = 0; i < 5; i++) { 293 | // User *user = [[User alloc] init]; 294 | // user.name = @"db更新"; 295 | // user.ID_no = [NSString stringWithFormat:@"%d",55+i]; 296 | // user.age = 55555+i; 297 | // user.ID = i +10; 298 | // [array addObject:user]; 299 | // [user release]; 300 | // } 301 | // [User updateObjectsByDB:array]; 302 | // }); 303 | //} 304 | // 305 | ///** 用db查询User数据*/ 306 | //- (IBAction)dbQueryData:(id)sender { 307 | // //查询全部 308 | // dispatch_async(dispatch_get_global_queue(0, 0), ^{ 309 | // [User findAllByDB]; 310 | // }); 311 | // //条件查询 312 | // dispatch_async(dispatch_get_global_queue(0, 0), ^{ 313 | // [User findBySqlByDB:@" WHERE age = 20 "]; 314 | // }); 315 | // //查询单个 316 | // dispatch_async(dispatch_get_global_queue(0, 0), ^{ 317 | // [User findFirstBySqlByDB:@" WHERE age = 20 "]; 318 | // }); 319 | //} 320 | 321 | @end 322 | -------------------------------------------------------------------------------- /JKDBModel/DBModel/JKDBHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // JKDataBase.h 3 | // JKBaseModel 4 | // 5 | // Created by zx_04 on 15/6/24. 6 | // 7 | // github:https://github.com/Joker-King/JKDBModel 8 | 9 | #import 10 | #import "FMDB.h" 11 | 12 | @interface JKDBHelper : NSObject 13 | 14 | @property (nonatomic, retain, readonly) FMDatabaseQueue *dbQueue; 15 | 16 | + (JKDBHelper *)shareInstance; 17 | 18 | + (NSString *)dbPath; 19 | 20 | - (BOOL)changeDBWithDirectoryName:(NSString *)directoryName; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /JKDBModel/DBModel/JKDBHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // JKDataBase.m 3 | // JKBaseModel 4 | // 5 | // Created by zx_04 on 15/6/24. 6 | // 7 | // github:https://github.com/Joker-King/JKDBModel 8 | 9 | #import 10 | 11 | #import "JKDBHelper.h" 12 | #import "JKDBModel.h" 13 | 14 | @interface JKDBHelper () 15 | 16 | @property (nonatomic, retain) FMDatabaseQueue *dbQueue; 17 | 18 | @end 19 | 20 | @implementation JKDBHelper 21 | 22 | static JKDBHelper *_instance = nil; 23 | 24 | + (instancetype)shareInstance 25 | { 26 | static dispatch_once_t onceToken ; 27 | dispatch_once(&onceToken, ^{ 28 | _instance = [[super allocWithZone:NULL] init] ; 29 | }) ; 30 | 31 | return _instance; 32 | } 33 | 34 | + (NSString *)dbPathWithDirectoryName:(NSString *)directoryName 35 | { 36 | NSString *docsdir = [NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; 37 | NSFileManager *filemanage = [NSFileManager defaultManager]; 38 | if (directoryName == nil || directoryName.length == 0) { 39 | docsdir = [docsdir stringByAppendingPathComponent:@"JKBD"]; 40 | } else { 41 | docsdir = [docsdir stringByAppendingPathComponent:directoryName]; 42 | } 43 | BOOL isDir; 44 | BOOL exit =[filemanage fileExistsAtPath:docsdir isDirectory:&isDir]; 45 | if (!exit || !isDir) { 46 | [filemanage createDirectoryAtPath:docsdir withIntermediateDirectories:YES attributes:nil error:nil]; 47 | } 48 | NSString *dbpath = [docsdir stringByAppendingPathComponent:@"jkdb.sqlite"]; 49 | return dbpath; 50 | } 51 | 52 | + (NSString *)dbPath 53 | { 54 | return [self dbPathWithDirectoryName:nil]; 55 | } 56 | 57 | - (FMDatabaseQueue *)dbQueue 58 | { 59 | if (_dbQueue == nil) { 60 | _dbQueue = [[FMDatabaseQueue alloc] initWithPath:[self.class dbPath]]; 61 | } 62 | return _dbQueue; 63 | } 64 | 65 | - (BOOL)changeDBWithDirectoryName:(NSString *)directoryName 66 | { 67 | if (_instance.dbQueue) { 68 | _instance.dbQueue = nil; 69 | } 70 | _instance.dbQueue = [[FMDatabaseQueue alloc] initWithPath:[JKDBHelper dbPathWithDirectoryName:directoryName]]; 71 | 72 | int numClasses; 73 | Class *classes = NULL; 74 | numClasses = objc_getClassList(NULL,0); 75 | 76 | if (numClasses >0 ) 77 | { 78 | classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numClasses); 79 | numClasses = objc_getClassList(classes, numClasses); 80 | for (int i = 0; i < numClasses; i++) { 81 | if (class_getSuperclass(classes[i]) == [JKDBModel class]){ 82 | id class = classes[i]; 83 | [class performSelector:@selector(createTable) withObject:nil]; 84 | } 85 | } 86 | free(classes); 87 | } 88 | 89 | return YES; 90 | } 91 | 92 | + (id)allocWithZone:(struct _NSZone *)zone 93 | { 94 | return [JKDBHelper shareInstance]; 95 | } 96 | 97 | - (id)copyWithZone:(struct _NSZone *)zone 98 | { 99 | return [JKDBHelper shareInstance]; 100 | } 101 | 102 | #if ! __has_feature(objc_arc) 103 | - (oneway void)release 104 | { 105 | 106 | } 107 | 108 | - (id)autorelease 109 | { 110 | return _instance; 111 | } 112 | 113 | - (NSUInteger)retainCount 114 | { 115 | return 1; 116 | } 117 | #endif 118 | 119 | @end 120 | -------------------------------------------------------------------------------- /JKDBModel/DBModel/JKDBModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // JKBaseModel.h 3 | // JKBaseModel 4 | // 5 | // Created by zx_04 on 15/6/27. 6 | // Copyright (c) 2015年 joker. All rights reserved. 7 | // github:https://github.com/Joker-King/JKDBModel 8 | 9 | #import 10 | 11 | /** SQLite五种数据类型 */ 12 | #define SQLTEXT @"TEXT" 13 | #define SQLINTEGER @"INTEGER" 14 | #define SQLREAL @"REAL" 15 | #define SQLBLOB @"BLOB" 16 | #define SQLNULL @"NULL" 17 | #define PrimaryKey @"primary key" 18 | 19 | #define primaryId @"pk" 20 | 21 | @interface JKDBModel : NSObject 22 | 23 | /** 主键 id */ 24 | @property (nonatomic, assign) int pk; 25 | /** 列名 */ 26 | @property (retain, readonly, nonatomic) NSMutableArray *columeNames; 27 | /** 列类型 */ 28 | @property (retain, readonly, nonatomic) NSMutableArray *columeTypes; 29 | 30 | /** 31 | * 获取该类的所有属性 32 | */ 33 | + (NSDictionary *)getPropertys; 34 | 35 | /** 获取所有属性,包括主键 */ 36 | + (NSDictionary *)getAllProperties; 37 | 38 | /** 数据库中是否存在表 */ 39 | + (BOOL)isExistInTable; 40 | 41 | /** 表中的字段*/ 42 | + (NSArray *)getColumns; 43 | 44 | /** 保存或更新 45 | * 如果不存在主键,保存, 46 | * 有主键,则更新 47 | */ 48 | - (BOOL)saveOrUpdate; 49 | /** 保存或更新 50 | * 如果根据特定的列数据可以获取记录,则更新, 51 | * 没有记录,则保存 52 | */ 53 | - (BOOL)saveOrUpdateByColumnName:(NSString*)columnName AndColumnValue:(NSString*)columnValue; 54 | /** 保存单个数据 */ 55 | - (BOOL)save; 56 | /** 批量保存数据 */ 57 | + (BOOL)saveObjects:(NSArray *)array; 58 | /** 更新单个数据 */ 59 | - (BOOL)update; 60 | /** 批量更新数据*/ 61 | + (BOOL)updateObjects:(NSArray *)array; 62 | /** 删除单个数据 */ 63 | - (BOOL)deleteObject; 64 | /** 批量删除数据 */ 65 | + (BOOL)deleteObjects:(NSArray *)array; 66 | /** 通过条件删除数据 */ 67 | + (BOOL)deleteObjectsByCriteria:(NSString *)criteria; 68 | /** 通过条件删除 (多参数)--2 */ 69 | + (BOOL)deleteObjectsWithFormat:(NSString *)format, ...; 70 | /** 清空表 */ 71 | + (BOOL)clearTable; 72 | 73 | /** 查询全部数据 */ 74 | + (NSArray *)findAll; 75 | 76 | /** 通过主键查询 */ 77 | + (instancetype)findByPK:(int)inPk; 78 | 79 | + (instancetype)findFirstWithFormat:(NSString *)format, ...; 80 | 81 | /** 查找某条数据 */ 82 | + (instancetype)findFirstByCriteria:(NSString *)criteria; 83 | 84 | + (NSArray *)findWithFormat:(NSString *)format, ...; 85 | 86 | /** 通过条件查找数据 87 | * 这样可以进行分页查询 @" WHERE pk > 5 limit 10" 88 | */ 89 | + (NSArray *)findByCriteria:(NSString *)criteria; 90 | /** 91 | * 创建表 92 | * 如果已经创建,返回YES 93 | */ 94 | + (BOOL)createTable; 95 | 96 | #pragma mark - must be override method 97 | /** 如果子类中有一些property不需要创建数据库字段,那么这个方法必须在子类中重写 98 | */ 99 | + (NSArray *)transients; 100 | 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /JKDBModel/DBModel/JKDBModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // JKBaseModel.m 3 | // JKBaseModel 4 | // 5 | // Created by zx_04 on 15/6/27. 6 | // Copyright (c) 2015年 joker. All rights reserved. 7 | // github:https://github.com/Joker-King/JKDBModel 8 | 9 | #import "JKDBModel.h" 10 | #import "JKDBHelper.h" 11 | 12 | #import 13 | 14 | @implementation JKDBModel 15 | 16 | #pragma mark - override method 17 | + (void)initialize 18 | { 19 | if (self != [JKDBModel self]) { 20 | [self createTable]; 21 | } 22 | } 23 | 24 | - (instancetype)init 25 | { 26 | self = [super init]; 27 | if (self) { 28 | NSDictionary *dic = [self.class getAllProperties]; 29 | _columeNames = [[NSMutableArray alloc] initWithArray:[dic objectForKey:@"name"]]; 30 | _columeTypes = [[NSMutableArray alloc] initWithArray:[dic objectForKey:@"type"]]; 31 | } 32 | 33 | return self; 34 | } 35 | 36 | #pragma mark - base method 37 | /** 38 | * 获取该类的所有属性 39 | */ 40 | + (NSDictionary *)getPropertys 41 | { 42 | NSMutableArray *proNames = [NSMutableArray array]; 43 | NSMutableArray *proTypes = [NSMutableArray array]; 44 | NSArray *theTransients = [[self class] transients]; 45 | unsigned int outCount, i; 46 | objc_property_t *properties = class_copyPropertyList([self class], &outCount); 47 | for (i = 0; i < outCount; i++) { 48 | objc_property_t property = properties[i]; 49 | //获取属性名 50 | NSString *propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding]; 51 | if ([theTransients containsObject:propertyName]) { 52 | continue; 53 | } 54 | [proNames addObject:propertyName]; 55 | //获取属性类型等参数 56 | NSString *propertyType = [NSString stringWithCString: property_getAttributes(property) encoding:NSUTF8StringEncoding]; 57 | /* 58 | 各种符号对应类型,部分类型在新版SDK中有所变化,如long 和long long 59 | c char C unsigned char 60 | i int I unsigned int 61 | l long L unsigned long 62 | s short S unsigned short 63 | d double D unsigned double 64 | f float F unsigned float 65 | q long long Q unsigned long long 66 | B BOOL 67 | @ 对象类型 //指针 对象类型 如NSString 是@“NSString” 68 | 69 | 70 | 64位下long 和long long 都是Tq 71 | SQLite 默认支持五种数据类型TEXT、INTEGER、REAL、BLOB、NULL 72 | 因为在项目中用的类型不多,故只考虑了少数类型 73 | */ 74 | if ([propertyType hasPrefix:@"T@\"NSString\""]) { 75 | [proTypes addObject:SQLTEXT]; 76 | } else if ([propertyType hasPrefix:@"T@\"NSData\""]) { 77 | [proTypes addObject:SQLBLOB]; 78 | } else if ([propertyType hasPrefix:@"Ti"]||[propertyType hasPrefix:@"TI"]||[propertyType hasPrefix:@"Ts"]||[propertyType hasPrefix:@"TS"]||[propertyType hasPrefix:@"TB"]||[propertyType hasPrefix:@"Tq"]||[propertyType hasPrefix:@"TQ"]) { 79 | [proTypes addObject:SQLINTEGER]; 80 | } else { 81 | [proTypes addObject:SQLREAL]; 82 | } 83 | 84 | } 85 | free(properties); 86 | 87 | return [NSDictionary dictionaryWithObjectsAndKeys:proNames,@"name",proTypes,@"type",nil]; 88 | } 89 | 90 | /** 获取所有属性,包含主键pk */ 91 | + (NSDictionary *)getAllProperties 92 | { 93 | NSDictionary *dict = [self.class getPropertys]; 94 | 95 | NSMutableArray *proNames = [NSMutableArray array]; 96 | NSMutableArray *proTypes = [NSMutableArray array]; 97 | [proNames addObject:primaryId]; 98 | [proTypes addObject:[NSString stringWithFormat:@"%@ %@",SQLINTEGER,PrimaryKey]]; 99 | [proNames addObjectsFromArray:[dict objectForKey:@"name"]]; 100 | [proTypes addObjectsFromArray:[dict objectForKey:@"type"]]; 101 | 102 | return [NSDictionary dictionaryWithObjectsAndKeys:proNames,@"name",proTypes,@"type",nil]; 103 | } 104 | 105 | /** 数据库中是否存在表 */ 106 | + (BOOL)isExistInTable 107 | { 108 | __block BOOL res = NO; 109 | JKDBHelper *jkDB = [JKDBHelper shareInstance]; 110 | [jkDB.dbQueue inDatabase:^(FMDatabase *db) { 111 | NSString *tableName = NSStringFromClass(self.class); 112 | res = [db tableExists:tableName]; 113 | }]; 114 | return res; 115 | } 116 | 117 | /** 获取列名 */ 118 | + (NSArray *)getColumns 119 | { 120 | JKDBHelper *jkDB = [JKDBHelper shareInstance]; 121 | NSMutableArray *columns = [NSMutableArray array]; 122 | [jkDB.dbQueue inDatabase:^(FMDatabase *db) { 123 | NSString *tableName = NSStringFromClass(self.class); 124 | FMResultSet *resultSet = [db getTableSchema:tableName]; 125 | while ([resultSet next]) { 126 | NSString *column = [resultSet stringForColumn:@"name"]; 127 | [columns addObject:column]; 128 | } 129 | }]; 130 | return [columns copy]; 131 | } 132 | 133 | /** 134 | * 创建表 135 | * 如果已经创建,返回YES 136 | */ 137 | + (BOOL)createTable 138 | { 139 | __block BOOL res = YES; 140 | JKDBHelper *jkDB = [JKDBHelper shareInstance]; 141 | [jkDB.dbQueue inTransaction:^(FMDatabase *db, BOOL *rollback) { 142 | NSString *tableName = NSStringFromClass(self.class); 143 | NSString *columeAndType = [self.class getColumeAndTypeString]; 144 | NSString *sql = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@(%@);",tableName,columeAndType]; 145 | if (![db executeUpdate:sql]) { 146 | res = NO; 147 | *rollback = YES; 148 | return; 149 | }; 150 | 151 | NSMutableArray *columns = [NSMutableArray array]; 152 | FMResultSet *resultSet = [db getTableSchema:tableName]; 153 | while ([resultSet next]) { 154 | NSString *column = [resultSet stringForColumn:@"name"]; 155 | [columns addObject:column]; 156 | } 157 | NSDictionary *dict = [self.class getAllProperties]; 158 | NSArray *properties = [dict objectForKey:@"name"]; 159 | NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"NOT (SELF IN %@)",columns]; 160 | //过滤数组 161 | NSArray *resultArray = [properties filteredArrayUsingPredicate:filterPredicate]; 162 | for (NSString *column in resultArray) { 163 | NSUInteger index = [properties indexOfObject:column]; 164 | NSString *proType = [[dict objectForKey:@"type"] objectAtIndex:index]; 165 | NSString *fieldSql = [NSString stringWithFormat:@"%@ %@",column,proType]; 166 | NSString *sql = [NSString stringWithFormat:@"ALTER TABLE %@ ADD COLUMN %@ ",NSStringFromClass(self.class),fieldSql]; 167 | if (![db executeUpdate:sql]) { 168 | res = NO; 169 | *rollback = YES; 170 | return ; 171 | } 172 | } 173 | }]; 174 | 175 | return res; 176 | } 177 | 178 | /** 179 | * 创建表 180 | * 如果已经创建,返回YES 181 | */ 182 | //+ (BOOL)createTable 183 | //{ 184 | // FMDatabase *db = [FMDatabase databaseWithPath:[JKDBHelper dbPath]]; 185 | // if (![db open]) { 186 | // NSLog(@"数据库打开失败!"); 187 | // return NO; 188 | // } 189 | // 190 | // NSString *tableName = NSStringFromClass(self.class); 191 | // NSString *columeAndType = [self.class getColumeAndTypeString]; 192 | // NSString *sql = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@(%@);",tableName,columeAndType]; 193 | // if (![db executeUpdate:sql]) { 194 | // return NO; 195 | // } 196 | // 197 | // NSMutableArray *columns = [NSMutableArray array]; 198 | // FMResultSet *resultSet = [db getTableSchema:tableName]; 199 | // while ([resultSet next]) { 200 | // NSString *column = [resultSet stringForColumn:@"name"]; 201 | // [columns addObject:column]; 202 | // } 203 | // NSDictionary *dict = [self.class getAllProperties]; 204 | // NSArray *properties = [dict objectForKey:@"name"]; 205 | // NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"NOT (SELF IN %@)",columns]; 206 | // //过滤数组 207 | // NSArray *resultArray = [properties filteredArrayUsingPredicate:filterPredicate]; 208 | // 209 | // for (NSString *column in resultArray) { 210 | // NSUInteger index = [properties indexOfObject:column]; 211 | // NSString *proType = [[dict objectForKey:@"type"] objectAtIndex:index]; 212 | // NSString *fieldSql = [NSString stringWithFormat:@"%@ %@",column,proType]; 213 | // NSString *sql = [NSString stringWithFormat:@"ALTER TABLE %@ ADD COLUMN %@ ",NSStringFromClass(self.class),fieldSql]; 214 | // if (![db executeUpdate:sql]) { 215 | // return NO; 216 | // } 217 | // } 218 | // [db close]; 219 | // return YES; 220 | //} 221 | 222 | - (BOOL)saveOrUpdate 223 | { 224 | id primaryValue = [self valueForKey:primaryId]; 225 | if ([primaryValue intValue] <= 0) { 226 | return [self save]; 227 | } 228 | 229 | return [self update]; 230 | } 231 | 232 | - (BOOL)saveOrUpdateByColumnName:(NSString*)columnName AndColumnValue:(NSString*)columnValue 233 | { 234 | id record = [self.class findFirstByCriteria:[NSString stringWithFormat:@"where %@ = %@",columnName,columnValue]]; 235 | if (record) { 236 | id primaryValue = [record valueForKey:primaryId]; //取到了主键PK 237 | if ([primaryValue intValue] <= 0) { 238 | return [self save]; 239 | }else{ 240 | self.pk = [primaryValue integerValue]; 241 | return [self update]; 242 | } 243 | }else{ 244 | return [self save]; 245 | } 246 | } 247 | 248 | - (BOOL)save 249 | { 250 | NSString *tableName = NSStringFromClass(self.class); 251 | NSMutableString *keyString = [NSMutableString string]; 252 | NSMutableString *valueString = [NSMutableString string]; 253 | NSMutableArray *insertValues = [NSMutableArray array]; 254 | for (int i = 0; i < self.columeNames.count; i++) { 255 | NSString *proname = [self.columeNames objectAtIndex:i]; 256 | if ([proname isEqualToString:primaryId]) { 257 | continue; 258 | } 259 | [keyString appendFormat:@"%@,", proname]; 260 | [valueString appendString:@"?,"]; 261 | id value = [self valueForKey:proname]; 262 | if (!value) { 263 | value = @""; 264 | } 265 | [insertValues addObject:value]; 266 | } 267 | 268 | [keyString deleteCharactersInRange:NSMakeRange(keyString.length - 1, 1)]; 269 | [valueString deleteCharactersInRange:NSMakeRange(valueString.length - 1, 1)]; 270 | 271 | JKDBHelper *jkDB = [JKDBHelper shareInstance]; 272 | __block BOOL res = NO; 273 | [jkDB.dbQueue inDatabase:^(FMDatabase *db) { 274 | NSString *sql = [NSString stringWithFormat:@"INSERT INTO %@(%@) VALUES (%@);", tableName, keyString, valueString]; 275 | res = [db executeUpdate:sql withArgumentsInArray:insertValues]; 276 | self.pk = res?[NSNumber numberWithLongLong:db.lastInsertRowId].intValue:0; 277 | NSLog(res?@"插入成功":@"插入失败"); 278 | }]; 279 | return res; 280 | } 281 | 282 | /** 批量保存用户对象 */ 283 | + (BOOL)saveObjects:(NSArray *)array 284 | { 285 | //判断是否是JKBaseModel的子类 286 | for (JKDBModel *model in array) { 287 | if (![model isKindOfClass:[JKDBModel class]]) { 288 | return NO; 289 | } 290 | } 291 | 292 | __block BOOL res = YES; 293 | JKDBHelper *jkDB = [JKDBHelper shareInstance]; 294 | // 如果要支持事务 295 | [jkDB.dbQueue inTransaction:^(FMDatabase *db, BOOL *rollback) { 296 | for (JKDBModel *model in array) { 297 | NSString *tableName = NSStringFromClass(model.class); 298 | NSMutableString *keyString = [NSMutableString string]; 299 | NSMutableString *valueString = [NSMutableString string]; 300 | NSMutableArray *insertValues = [NSMutableArray array]; 301 | for (int i = 0; i < model.columeNames.count; i++) { 302 | NSString *proname = [model.columeNames objectAtIndex:i]; 303 | if ([proname isEqualToString:primaryId]) { 304 | continue; 305 | } 306 | [keyString appendFormat:@"%@,", proname]; 307 | [valueString appendString:@"?,"]; 308 | id value = [model valueForKey:proname]; 309 | if (!value) { 310 | value = @""; 311 | } 312 | [insertValues addObject:value]; 313 | } 314 | [keyString deleteCharactersInRange:NSMakeRange(keyString.length - 1, 1)]; 315 | [valueString deleteCharactersInRange:NSMakeRange(valueString.length - 1, 1)]; 316 | 317 | NSString *sql = [NSString stringWithFormat:@"INSERT INTO %@(%@) VALUES (%@);", tableName, keyString, valueString]; 318 | BOOL flag = [db executeUpdate:sql withArgumentsInArray:insertValues]; 319 | model.pk = flag?[NSNumber numberWithLongLong:db.lastInsertRowId].intValue:0; 320 | NSLog(flag?@"插入成功":@"插入失败"); 321 | if (!flag) { 322 | res = NO; 323 | *rollback = YES; 324 | return; 325 | } 326 | } 327 | }]; 328 | return res; 329 | } 330 | 331 | /** 更新单个对象 */ 332 | - (BOOL)update 333 | { 334 | JKDBHelper *jkDB = [JKDBHelper shareInstance]; 335 | __block BOOL res = NO; 336 | [jkDB.dbQueue inDatabase:^(FMDatabase *db) { 337 | NSString *tableName = NSStringFromClass(self.class); 338 | id primaryValue = [self valueForKey:primaryId]; 339 | if (!primaryValue || primaryValue <= 0) { 340 | return ; 341 | } 342 | NSMutableString *keyString = [NSMutableString string]; 343 | NSMutableArray *updateValues = [NSMutableArray array]; 344 | for (int i = 0; i < self.columeNames.count; i++) { 345 | NSString *proname = [self.columeNames objectAtIndex:i]; 346 | if ([proname isEqualToString:primaryId]) { 347 | continue; 348 | } 349 | [keyString appendFormat:@" %@=?,", proname]; 350 | id value = [self valueForKey:proname]; 351 | if (!value) { 352 | value = @""; 353 | } 354 | [updateValues addObject:value]; 355 | } 356 | 357 | //删除最后那个逗号 358 | [keyString deleteCharactersInRange:NSMakeRange(keyString.length - 1, 1)]; 359 | NSString *sql = [NSString stringWithFormat:@"UPDATE %@ SET %@ WHERE %@ = ?;", tableName, keyString, primaryId]; 360 | [updateValues addObject:primaryValue]; 361 | res = [db executeUpdate:sql withArgumentsInArray:updateValues]; 362 | NSLog(res?@"更新成功":@"更新失败"); 363 | }]; 364 | return res; 365 | } 366 | 367 | /** 批量更新用户对象*/ 368 | + (BOOL)updateObjects:(NSArray *)array 369 | { 370 | for (JKDBModel *model in array) { 371 | if (![model isKindOfClass:[JKDBModel class]]) { 372 | return NO; 373 | } 374 | } 375 | __block BOOL res = YES; 376 | JKDBHelper *jkDB = [JKDBHelper shareInstance]; 377 | // 如果要支持事务 378 | [jkDB.dbQueue inTransaction:^(FMDatabase *db, BOOL *rollback) { 379 | for (JKDBModel *model in array) { 380 | NSString *tableName = NSStringFromClass(model.class); 381 | id primaryValue = [model valueForKey:primaryId]; 382 | if (!primaryValue || primaryValue <= 0) { 383 | res = NO; 384 | *rollback = YES; 385 | return; 386 | } 387 | 388 | NSMutableString *keyString = [NSMutableString string]; 389 | NSMutableArray *updateValues = [NSMutableArray array]; 390 | for (int i = 0; i < model.columeNames.count; i++) { 391 | NSString *proname = [model.columeNames objectAtIndex:i]; 392 | if ([proname isEqualToString:primaryId]) { 393 | continue; 394 | } 395 | [keyString appendFormat:@" %@=?,", proname]; 396 | id value = [model valueForKey:proname]; 397 | if (!value) { 398 | value = @""; 399 | } 400 | [updateValues addObject:value]; 401 | } 402 | 403 | //删除最后那个逗号 404 | [keyString deleteCharactersInRange:NSMakeRange(keyString.length - 1, 1)]; 405 | NSString *sql = [NSString stringWithFormat:@"UPDATE %@ SET %@ WHERE %@=?;", tableName, keyString, primaryId]; 406 | [updateValues addObject:primaryValue]; 407 | BOOL flag = [db executeUpdate:sql withArgumentsInArray:updateValues]; 408 | NSLog(flag?@"更新成功":@"更新失败"); 409 | if (!flag) { 410 | res = NO; 411 | *rollback = YES; 412 | return; 413 | } 414 | } 415 | }]; 416 | 417 | return res; 418 | } 419 | 420 | /** 删除单个对象 */ 421 | - (BOOL)deleteObject 422 | { 423 | JKDBHelper *jkDB = [JKDBHelper shareInstance]; 424 | __block BOOL res = NO; 425 | [jkDB.dbQueue inDatabase:^(FMDatabase *db) { 426 | NSString *tableName = NSStringFromClass(self.class); 427 | id primaryValue = [self valueForKey:primaryId]; 428 | if (!primaryValue || primaryValue <= 0) { 429 | return ; 430 | } 431 | NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ = ?",tableName,primaryId]; 432 | res = [db executeUpdate:sql withArgumentsInArray:@[primaryValue]]; 433 | NSLog(res?@"删除成功":@"删除失败"); 434 | }]; 435 | return res; 436 | } 437 | 438 | /** 批量删除用户对象 */ 439 | + (BOOL)deleteObjects:(NSArray *)array 440 | { 441 | for (JKDBModel *model in array) { 442 | if (![model isKindOfClass:[JKDBModel class]]) { 443 | return NO; 444 | } 445 | } 446 | 447 | __block BOOL res = YES; 448 | JKDBHelper *jkDB = [JKDBHelper shareInstance]; 449 | // 如果要支持事务 450 | [jkDB.dbQueue inTransaction:^(FMDatabase *db, BOOL *rollback) { 451 | for (JKDBModel *model in array) { 452 | NSString *tableName = NSStringFromClass(model.class); 453 | id primaryValue = [model valueForKey:primaryId]; 454 | if (!primaryValue || primaryValue <= 0) { 455 | return ; 456 | } 457 | 458 | NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ = ?",tableName,primaryId]; 459 | BOOL flag = [db executeUpdate:sql withArgumentsInArray:@[primaryValue]]; 460 | NSLog(flag?@"删除成功":@"删除失败"); 461 | if (!flag) { 462 | res = NO; 463 | *rollback = YES; 464 | return; 465 | } 466 | } 467 | }]; 468 | return res; 469 | } 470 | 471 | /** 通过条件删除数据 */ 472 | + (BOOL)deleteObjectsByCriteria:(NSString *)criteria 473 | { 474 | JKDBHelper *jkDB = [JKDBHelper shareInstance]; 475 | __block BOOL res = NO; 476 | [jkDB.dbQueue inDatabase:^(FMDatabase *db) { 477 | NSString *tableName = NSStringFromClass(self.class); 478 | NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@ %@ ",tableName,criteria]; 479 | res = [db executeUpdate:sql]; 480 | NSLog(res?@"删除成功":@"删除失败"); 481 | }]; 482 | return res; 483 | } 484 | 485 | /** 通过条件删除 (多参数)--2 */ 486 | + (BOOL)deleteObjectsWithFormat:(NSString *)format, ... 487 | { 488 | va_list ap; 489 | va_start(ap, format); 490 | NSString *criteria = [[NSString alloc] initWithFormat:format locale:[NSLocale currentLocale] arguments:ap]; 491 | va_end(ap); 492 | 493 | return [self deleteObjectsByCriteria:criteria]; 494 | } 495 | 496 | /** 清空表 */ 497 | + (BOOL)clearTable 498 | { 499 | JKDBHelper *jkDB = [JKDBHelper shareInstance]; 500 | __block BOOL res = NO; 501 | [jkDB.dbQueue inDatabase:^(FMDatabase *db) { 502 | NSString *tableName = NSStringFromClass(self.class); 503 | NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@",tableName]; 504 | res = [db executeUpdate:sql]; 505 | NSLog(res?@"清空成功":@"清空失败"); 506 | }]; 507 | return res; 508 | } 509 | 510 | /** 查询全部数据 */ 511 | + (NSArray *)findAll 512 | { 513 | NSLog(@"jkdb---%s",__func__); 514 | JKDBHelper *jkDB = [JKDBHelper shareInstance]; 515 | NSMutableArray *users = [NSMutableArray array]; 516 | [jkDB.dbQueue inDatabase:^(FMDatabase *db) { 517 | NSString *tableName = NSStringFromClass(self.class); 518 | NSString *sql = [NSString stringWithFormat:@"SELECT * FROM %@",tableName]; 519 | FMResultSet *resultSet = [db executeQuery:sql]; 520 | while ([resultSet next]) { 521 | JKDBModel *model = [[self.class alloc] init]; 522 | for (int i=0; i< model.columeNames.count; i++) { 523 | NSString *columeName = [model.columeNames objectAtIndex:i]; 524 | NSString *columeType = [model.columeTypes objectAtIndex:i]; 525 | if ([columeType isEqualToString:SQLTEXT]) { 526 | [model setValue:[resultSet stringForColumn:columeName] forKey:columeName]; 527 | } else if ([columeType isEqualToString:SQLBLOB]) { 528 | [model setValue:[resultSet dataForColumn:columeName] forKey:columeName]; 529 | } else { 530 | [model setValue:[NSNumber numberWithLongLong:[resultSet longLongIntForColumn:columeName]] forKey:columeName]; 531 | } 532 | } 533 | [users addObject:model]; 534 | FMDBRelease(model); 535 | } 536 | }]; 537 | 538 | return users; 539 | } 540 | 541 | + (instancetype)findFirstWithFormat:(NSString *)format, ... 542 | { 543 | va_list ap; 544 | va_start(ap, format); 545 | NSString *criteria = [[NSString alloc] initWithFormat:format locale:[NSLocale currentLocale] arguments:ap]; 546 | va_end(ap); 547 | 548 | return [self findFirstByCriteria:criteria]; 549 | } 550 | 551 | /** 查找某条数据 */ 552 | + (instancetype)findFirstByCriteria:(NSString *)criteria 553 | { 554 | NSArray *results = [self.class findByCriteria:criteria]; 555 | if (results.count < 1) { 556 | return nil; 557 | } 558 | 559 | return [results firstObject]; 560 | } 561 | 562 | + (instancetype)findByPK:(int)inPk 563 | { 564 | NSString *condition = [NSString stringWithFormat:@"WHERE %@=%d",primaryId,inPk]; 565 | return [self findFirstByCriteria:condition]; 566 | } 567 | 568 | + (NSArray *)findWithFormat:(NSString *)format, ... 569 | { 570 | va_list ap; 571 | va_start(ap, format); 572 | NSString *criteria = [[NSString alloc] initWithFormat:format locale:[NSLocale currentLocale] arguments:ap]; 573 | va_end(ap); 574 | 575 | return [self findByCriteria:criteria]; 576 | } 577 | 578 | /** 通过条件查找数据 */ 579 | + (NSArray *)findByCriteria:(NSString *)criteria 580 | { 581 | JKDBHelper *jkDB = [JKDBHelper shareInstance]; 582 | NSMutableArray *users = [NSMutableArray array]; 583 | [jkDB.dbQueue inDatabase:^(FMDatabase *db) { 584 | NSString *tableName = NSStringFromClass(self.class); 585 | NSString *sql = [NSString stringWithFormat:@"SELECT * FROM %@ %@",tableName,criteria]; 586 | FMResultSet *resultSet = [db executeQuery:sql]; 587 | while ([resultSet next]) { 588 | JKDBModel *model = [[self.class alloc] init]; 589 | for (int i=0; i< model.columeNames.count; i++) { 590 | NSString *columeName = [model.columeNames objectAtIndex:i]; 591 | NSString *columeType = [model.columeTypes objectAtIndex:i]; 592 | if ([columeType isEqualToString:SQLTEXT]) { 593 | [model setValue:[resultSet stringForColumn:columeName] forKey:columeName]; 594 | } else if ([columeType isEqualToString:SQLBLOB]) { 595 | [model setValue:[resultSet dataForColumn:columeName] forKey:columeName]; 596 | } else { 597 | [model setValue:[NSNumber numberWithLongLong:[resultSet longLongIntForColumn:columeName]] forKey:columeName]; 598 | } 599 | } 600 | [users addObject:model]; 601 | FMDBRelease(model); 602 | } 603 | }]; 604 | 605 | return users; 606 | } 607 | 608 | #pragma mark - util method 609 | + (NSString *)getColumeAndTypeString 610 | { 611 | NSMutableString* pars = [NSMutableString string]; 612 | NSDictionary *dict = [self.class getAllProperties]; 613 | 614 | NSMutableArray *proNames = [dict objectForKey:@"name"]; 615 | NSMutableArray *proTypes = [dict objectForKey:@"type"]; 616 | 617 | for (int i=0; i< proNames.count; i++) { 618 | [pars appendFormat:@"%@ %@",[proNames objectAtIndex:i],[proTypes objectAtIndex:i]]; 619 | if(i+1 != proNames.count) 620 | { 621 | [pars appendString:@","]; 622 | } 623 | } 624 | return pars; 625 | } 626 | 627 | - (NSString *)description 628 | { 629 | NSString *result = @""; 630 | NSDictionary *dict = [self.class getAllProperties]; 631 | NSMutableArray *proNames = [dict objectForKey:@"name"]; 632 | for (int i = 0; i < proNames.count; i++) { 633 | NSString *proName = [proNames objectAtIndex:i]; 634 | id proValue = [self valueForKey:proName]; 635 | result = [result stringByAppendingFormat:@"%@:%@\n",proName,proValue]; 636 | } 637 | return result; 638 | } 639 | 640 | #pragma mark - must be override method 641 | /** 如果子类中有一些property不需要创建数据库字段,那么这个方法必须在子类中重写 642 | */ 643 | + (NSArray *)transients 644 | { 645 | return [NSArray array]; 646 | } 647 | 648 | @end 649 | -------------------------------------------------------------------------------- /JKDBModel/FMDB/FMDB.h: -------------------------------------------------------------------------------- 1 | #import "FMDatabase.h" 2 | #import "FMResultSet.h" 3 | #import "FMDatabaseAdditions.h" 4 | #import "FMDatabaseQueue.h" 5 | #import "FMDatabasePool.h" 6 | -------------------------------------------------------------------------------- /JKDBModel/FMDB/FMDatabaseAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabaseAdditions.h 3 | // fmdb 4 | // 5 | // Created by August Mueller on 10/30/05. 6 | // Copyright 2005 Flying Meat Inc.. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "FMDatabase.h" 11 | 12 | 13 | /** Category of additions for `` class. 14 | 15 | ### See also 16 | 17 | - `` 18 | */ 19 | 20 | @interface FMDatabase (FMDatabaseAdditions) 21 | 22 | ///---------------------------------------- 23 | /// @name Return results of SQL to variable 24 | ///---------------------------------------- 25 | 26 | /** Return `int` value for query 27 | 28 | @param query The SQL query to be performed. 29 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 30 | 31 | @return `int` value. 32 | 33 | @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. 34 | */ 35 | 36 | - (int)intForQuery:(NSString*)query, ...; 37 | 38 | /** Return `long` value for query 39 | 40 | @param query The SQL query to be performed. 41 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 42 | 43 | @return `long` value. 44 | 45 | @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. 46 | */ 47 | 48 | - (long)longForQuery:(NSString*)query, ...; 49 | 50 | /** Return `BOOL` value for query 51 | 52 | @param query The SQL query to be performed. 53 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 54 | 55 | @return `BOOL` value. 56 | 57 | @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. 58 | */ 59 | 60 | - (BOOL)boolForQuery:(NSString*)query, ...; 61 | 62 | /** Return `double` value for query 63 | 64 | @param query The SQL query to be performed. 65 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 66 | 67 | @return `double` value. 68 | 69 | @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. 70 | */ 71 | 72 | - (double)doubleForQuery:(NSString*)query, ...; 73 | 74 | /** Return `NSString` value for query 75 | 76 | @param query The SQL query to be performed. 77 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 78 | 79 | @return `NSString` value. 80 | 81 | @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. 82 | */ 83 | 84 | - (NSString*)stringForQuery:(NSString*)query, ...; 85 | 86 | /** Return `NSData` value for query 87 | 88 | @param query The SQL query to be performed. 89 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 90 | 91 | @return `NSData` value. 92 | 93 | @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. 94 | */ 95 | 96 | - (NSData*)dataForQuery:(NSString*)query, ...; 97 | 98 | /** Return `NSDate` value for query 99 | 100 | @param query The SQL query to be performed. 101 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 102 | 103 | @return `NSDate` value. 104 | 105 | @note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project. 106 | */ 107 | 108 | - (NSDate*)dateForQuery:(NSString*)query, ...; 109 | 110 | 111 | // Notice that there's no dataNoCopyForQuery:. 112 | // That would be a bad idea, because we close out the result set, and then what 113 | // happens to the data that we just didn't copy? Who knows, not I. 114 | 115 | 116 | ///-------------------------------- 117 | /// @name Schema related operations 118 | ///-------------------------------- 119 | 120 | /** Does table exist in database? 121 | 122 | @param tableName The name of the table being looked for. 123 | 124 | @return `YES` if table found; `NO` if not found. 125 | */ 126 | 127 | - (BOOL)tableExists:(NSString*)tableName; 128 | 129 | /** The schema of the database. 130 | 131 | This will be the schema for the entire database. For each entity, each row of the result set will include the following fields: 132 | 133 | - `type` - The type of entity (e.g. table, index, view, or trigger) 134 | - `name` - The name of the object 135 | - `tbl_name` - The name of the table to which the object references 136 | - `rootpage` - The page number of the root b-tree page for tables and indices 137 | - `sql` - The SQL that created the entity 138 | 139 | @return `FMResultSet` of schema; `nil` on error. 140 | 141 | @see [SQLite File Format](http://www.sqlite.org/fileformat.html) 142 | */ 143 | 144 | - (FMResultSet*)getSchema; 145 | 146 | /** The schema of the database. 147 | 148 | This will be the schema for a particular table as report by SQLite `PRAGMA`, for example: 149 | 150 | PRAGMA table_info('employees') 151 | 152 | This will report: 153 | 154 | - `cid` - The column ID number 155 | - `name` - The name of the column 156 | - `type` - The data type specified for the column 157 | - `notnull` - whether the field is defined as NOT NULL (i.e. values required) 158 | - `dflt_value` - The default value for the column 159 | - `pk` - Whether the field is part of the primary key of the table 160 | 161 | @param tableName The name of the table for whom the schema will be returned. 162 | 163 | @return `FMResultSet` of schema; `nil` on error. 164 | 165 | @see [table_info](http://www.sqlite.org/pragma.html#pragma_table_info) 166 | */ 167 | 168 | - (FMResultSet*)getTableSchema:(NSString*)tableName; 169 | 170 | /** Test to see if particular column exists for particular table in database 171 | 172 | @param columnName The name of the column. 173 | 174 | @param tableName The name of the table. 175 | 176 | @return `YES` if column exists in table in question; `NO` otherwise. 177 | */ 178 | 179 | - (BOOL)columnExists:(NSString*)columnName inTableWithName:(NSString*)tableName; 180 | 181 | /** Test to see if particular column exists for particular table in database 182 | 183 | @param columnName The name of the column. 184 | 185 | @param tableName The name of the table. 186 | 187 | @return `YES` if column exists in table in question; `NO` otherwise. 188 | 189 | @see columnExists:inTableWithName: 190 | 191 | @warning Deprecated - use `` instead. 192 | */ 193 | 194 | - (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName __attribute__ ((deprecated)); 195 | 196 | 197 | /** Validate SQL statement 198 | 199 | This validates SQL statement by performing `sqlite3_prepare_v2`, but not returning the results, but instead immediately calling `sqlite3_finalize`. 200 | 201 | @param sql The SQL statement being validated. 202 | 203 | @param error This is a pointer to a `NSError` object that will receive the autoreleased `NSError` object if there was any error. If this is `nil`, no `NSError` result will be returned. 204 | 205 | @return `YES` if validation succeeded without incident; `NO` otherwise. 206 | 207 | */ 208 | 209 | - (BOOL)validateSQL:(NSString*)sql error:(NSError**)error; 210 | 211 | 212 | #if SQLITE_VERSION_NUMBER >= 3007017 213 | 214 | ///----------------------------------- 215 | /// @name Application identifier tasks 216 | ///----------------------------------- 217 | 218 | /** Retrieve application ID 219 | 220 | @return The `uint32_t` numeric value of the application ID. 221 | 222 | @see setApplicationID: 223 | */ 224 | 225 | - (uint32_t)applicationID; 226 | 227 | /** Set the application ID 228 | 229 | @param appID The `uint32_t` numeric value of the application ID. 230 | 231 | @see applicationID 232 | */ 233 | 234 | - (void)setApplicationID:(uint32_t)appID; 235 | 236 | #if TARGET_OS_MAC && !TARGET_OS_IPHONE 237 | /** Retrieve application ID string 238 | 239 | @return The `NSString` value of the application ID. 240 | 241 | @see setApplicationIDString: 242 | */ 243 | 244 | 245 | - (NSString*)applicationIDString; 246 | 247 | /** Set the application ID string 248 | 249 | @param string The `NSString` value of the application ID. 250 | 251 | @see applicationIDString 252 | */ 253 | 254 | - (void)setApplicationIDString:(NSString*)string; 255 | #endif 256 | 257 | #endif 258 | 259 | ///----------------------------------- 260 | /// @name user version identifier tasks 261 | ///----------------------------------- 262 | 263 | /** Retrieve user version 264 | 265 | @return The `uint32_t` numeric value of the user version. 266 | 267 | @see setUserVersion: 268 | */ 269 | 270 | - (uint32_t)userVersion; 271 | 272 | /** Set the user-version 273 | 274 | @param version The `uint32_t` numeric value of the user version. 275 | 276 | @see userVersion 277 | */ 278 | 279 | - (void)setUserVersion:(uint32_t)version; 280 | 281 | @end 282 | -------------------------------------------------------------------------------- /JKDBModel/FMDB/FMDatabaseAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabaseAdditions.m 3 | // fmdb 4 | // 5 | // Created by August Mueller on 10/30/05. 6 | // Copyright 2005 Flying Meat Inc.. All rights reserved. 7 | // 8 | 9 | #import "FMDatabase.h" 10 | #import "FMDatabaseAdditions.h" 11 | #import "TargetConditionals.h" 12 | 13 | @interface FMDatabase (PrivateStuff) 14 | - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args; 15 | @end 16 | 17 | @implementation FMDatabase (FMDatabaseAdditions) 18 | 19 | #define RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(type, sel) \ 20 | va_list args; \ 21 | va_start(args, query); \ 22 | FMResultSet *resultSet = [self executeQuery:query withArgumentsInArray:0x00 orDictionary:0x00 orVAList:args]; \ 23 | va_end(args); \ 24 | if (![resultSet next]) { return (type)0; } \ 25 | type ret = [resultSet sel:0]; \ 26 | [resultSet close]; \ 27 | [resultSet setParentDB:nil]; \ 28 | return ret; 29 | 30 | 31 | - (NSString*)stringForQuery:(NSString*)query, ... { 32 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSString *, stringForColumnIndex); 33 | } 34 | 35 | - (int)intForQuery:(NSString*)query, ... { 36 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(int, intForColumnIndex); 37 | } 38 | 39 | - (long)longForQuery:(NSString*)query, ... { 40 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(long, longForColumnIndex); 41 | } 42 | 43 | - (BOOL)boolForQuery:(NSString*)query, ... { 44 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(BOOL, boolForColumnIndex); 45 | } 46 | 47 | - (double)doubleForQuery:(NSString*)query, ... { 48 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(double, doubleForColumnIndex); 49 | } 50 | 51 | - (NSData*)dataForQuery:(NSString*)query, ... { 52 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSData *, dataForColumnIndex); 53 | } 54 | 55 | - (NSDate*)dateForQuery:(NSString*)query, ... { 56 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSDate *, dateForColumnIndex); 57 | } 58 | 59 | 60 | - (BOOL)tableExists:(NSString*)tableName { 61 | 62 | tableName = [tableName lowercaseString]; 63 | 64 | FMResultSet *rs = [self executeQuery:@"select [sql] from sqlite_master where [type] = 'table' and lower(name) = ?", tableName]; 65 | 66 | //if at least one next exists, table exists 67 | BOOL returnBool = [rs next]; 68 | 69 | //close and free object 70 | [rs close]; 71 | 72 | return returnBool; 73 | } 74 | 75 | /* 76 | get table with list of tables: result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING] 77 | check if table exist in database (patch from OZLB) 78 | */ 79 | - (FMResultSet*)getSchema { 80 | 81 | //result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING] 82 | FMResultSet *rs = [self executeQuery:@"SELECT type, name, tbl_name, rootpage, sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type != 'meta' AND name NOT LIKE 'sqlite_%' ORDER BY tbl_name, type DESC, name"]; 83 | 84 | return rs; 85 | } 86 | 87 | /* 88 | get table schema: result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER] 89 | */ 90 | - (FMResultSet*)getTableSchema:(NSString*)tableName { 91 | 92 | //result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER] 93 | FMResultSet *rs = [self executeQuery:[NSString stringWithFormat: @"pragma table_info('%@')", tableName]]; 94 | 95 | return rs; 96 | } 97 | 98 | - (BOOL)columnExists:(NSString*)columnName inTableWithName:(NSString*)tableName { 99 | 100 | BOOL returnBool = NO; 101 | 102 | tableName = [tableName lowercaseString]; 103 | columnName = [columnName lowercaseString]; 104 | 105 | FMResultSet *rs = [self getTableSchema:tableName]; 106 | 107 | //check if column is present in table schema 108 | while ([rs next]) { 109 | if ([[[rs stringForColumn:@"name"] lowercaseString] isEqualToString:columnName]) { 110 | returnBool = YES; 111 | break; 112 | } 113 | } 114 | 115 | //If this is not done FMDatabase instance stays out of pool 116 | [rs close]; 117 | 118 | return returnBool; 119 | } 120 | 121 | 122 | #if SQLITE_VERSION_NUMBER >= 3007017 123 | 124 | - (uint32_t)applicationID { 125 | 126 | uint32_t r = 0; 127 | 128 | FMResultSet *rs = [self executeQuery:@"pragma application_id"]; 129 | 130 | if ([rs next]) { 131 | r = (uint32_t)[rs longLongIntForColumnIndex:0]; 132 | } 133 | 134 | [rs close]; 135 | 136 | return r; 137 | } 138 | 139 | - (void)setApplicationID:(uint32_t)appID { 140 | NSString *query = [NSString stringWithFormat:@"pragma application_id=%d", appID]; 141 | FMResultSet *rs = [self executeQuery:query]; 142 | [rs next]; 143 | [rs close]; 144 | } 145 | 146 | 147 | #if TARGET_OS_MAC && !TARGET_OS_IPHONE 148 | - (NSString*)applicationIDString { 149 | NSString *s = NSFileTypeForHFSTypeCode([self applicationID]); 150 | 151 | assert([s length] == 6); 152 | 153 | s = [s substringWithRange:NSMakeRange(1, 4)]; 154 | 155 | 156 | return s; 157 | 158 | } 159 | 160 | - (void)setApplicationIDString:(NSString*)s { 161 | 162 | if ([s length] != 4) { 163 | NSLog(@"setApplicationIDString: string passed is not exactly 4 chars long. (was %ld)", [s length]); 164 | } 165 | 166 | [self setApplicationID:NSHFSTypeCodeFromFileType([NSString stringWithFormat:@"'%@'", s])]; 167 | } 168 | 169 | 170 | #endif 171 | 172 | #endif 173 | 174 | - (uint32_t)userVersion { 175 | uint32_t r = 0; 176 | 177 | FMResultSet *rs = [self executeQuery:@"pragma user_version"]; 178 | 179 | if ([rs next]) { 180 | r = (uint32_t)[rs longLongIntForColumnIndex:0]; 181 | } 182 | 183 | [rs close]; 184 | return r; 185 | } 186 | 187 | - (void)setUserVersion:(uint32_t)version { 188 | NSString *query = [NSString stringWithFormat:@"pragma user_version = %d", version]; 189 | FMResultSet *rs = [self executeQuery:query]; 190 | [rs next]; 191 | [rs close]; 192 | } 193 | 194 | #pragma clang diagnostic push 195 | #pragma clang diagnostic ignored "-Wdeprecated-implementations" 196 | 197 | - (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName __attribute__ ((deprecated)) { 198 | return [self columnExists:columnName inTableWithName:tableName]; 199 | } 200 | 201 | #pragma clang diagnostic pop 202 | 203 | 204 | - (BOOL)validateSQL:(NSString*)sql error:(NSError**)error { 205 | sqlite3_stmt *pStmt = NULL; 206 | BOOL validationSucceeded = YES; 207 | 208 | int rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0); 209 | if (rc != SQLITE_OK) { 210 | validationSucceeded = NO; 211 | if (error) { 212 | *error = [NSError errorWithDomain:NSCocoaErrorDomain 213 | code:[self lastErrorCode] 214 | userInfo:[NSDictionary dictionaryWithObject:[self lastErrorMessage] 215 | forKey:NSLocalizedDescriptionKey]]; 216 | } 217 | } 218 | 219 | sqlite3_finalize(pStmt); 220 | 221 | return validationSucceeded; 222 | } 223 | 224 | @end 225 | -------------------------------------------------------------------------------- /JKDBModel/FMDB/FMDatabasePool.h: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabasePool.h 3 | // fmdb 4 | // 5 | // Created by August Mueller on 6/22/11. 6 | // Copyright 2011 Flying Meat Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "sqlite3.h" 11 | 12 | @class FMDatabase; 13 | 14 | /** Pool of `` objects. 15 | 16 | ### See also 17 | 18 | - `` 19 | - `` 20 | 21 | @warning Before using `FMDatabasePool`, please consider using `` instead. 22 | 23 | If you really really really know what you're doing and `FMDatabasePool` is what 24 | you really really need (ie, you're using a read only database), OK you can use 25 | it. But just be careful not to deadlock! 26 | 27 | For an example on deadlocking, search for: 28 | `ONLY_USE_THE_POOL_IF_YOU_ARE_DOING_READS_OTHERWISE_YOULL_DEADLOCK_USE_FMDATABASEQUEUE_INSTEAD` 29 | in the main.m file. 30 | */ 31 | 32 | @interface FMDatabasePool : NSObject { 33 | NSString *_path; 34 | 35 | dispatch_queue_t _lockQueue; 36 | 37 | NSMutableArray *_databaseInPool; 38 | NSMutableArray *_databaseOutPool; 39 | 40 | __unsafe_unretained id _delegate; 41 | 42 | NSUInteger _maximumNumberOfDatabasesToCreate; 43 | int _openFlags; 44 | } 45 | 46 | /** Database path */ 47 | 48 | @property (atomic, retain) NSString *path; 49 | 50 | /** Delegate object */ 51 | 52 | @property (atomic, assign) id delegate; 53 | 54 | /** Maximum number of databases to create */ 55 | 56 | @property (atomic, assign) NSUInteger maximumNumberOfDatabasesToCreate; 57 | 58 | /** Open flags */ 59 | 60 | @property (atomic, readonly) int openFlags; 61 | 62 | 63 | ///--------------------- 64 | /// @name Initialization 65 | ///--------------------- 66 | 67 | /** Create pool using path. 68 | 69 | @param aPath The file path of the database. 70 | 71 | @return The `FMDatabasePool` object. `nil` on error. 72 | */ 73 | 74 | + (instancetype)databasePoolWithPath:(NSString*)aPath; 75 | 76 | /** Create pool using path and specified flags 77 | 78 | @param aPath The file path of the database. 79 | @param openFlags Flags passed to the openWithFlags method of the database 80 | 81 | @return The `FMDatabasePool` object. `nil` on error. 82 | */ 83 | 84 | + (instancetype)databasePoolWithPath:(NSString*)aPath flags:(int)openFlags; 85 | 86 | /** Create pool using path. 87 | 88 | @param aPath The file path of the database. 89 | 90 | @return The `FMDatabasePool` object. `nil` on error. 91 | */ 92 | 93 | - (instancetype)initWithPath:(NSString*)aPath; 94 | 95 | /** Create pool using path and specified flags. 96 | 97 | @param aPath The file path of the database. 98 | @param openFlags Flags passed to the openWithFlags method of the database 99 | 100 | @return The `FMDatabasePool` object. `nil` on error. 101 | */ 102 | 103 | - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags; 104 | 105 | ///------------------------------------------------ 106 | /// @name Keeping track of checked in/out databases 107 | ///------------------------------------------------ 108 | 109 | /** Number of checked-in databases in pool 110 | 111 | @returns Number of databases 112 | */ 113 | 114 | - (NSUInteger)countOfCheckedInDatabases; 115 | 116 | /** Number of checked-out databases in pool 117 | 118 | @returns Number of databases 119 | */ 120 | 121 | - (NSUInteger)countOfCheckedOutDatabases; 122 | 123 | /** Total number of databases in pool 124 | 125 | @returns Number of databases 126 | */ 127 | 128 | - (NSUInteger)countOfOpenDatabases; 129 | 130 | /** Release all databases in pool */ 131 | 132 | - (void)releaseAllDatabases; 133 | 134 | ///------------------------------------------ 135 | /// @name Perform database operations in pool 136 | ///------------------------------------------ 137 | 138 | /** Synchronously perform database operations in pool. 139 | 140 | @param block The code to be run on the `FMDatabasePool` pool. 141 | */ 142 | 143 | - (void)inDatabase:(void (^)(FMDatabase *db))block; 144 | 145 | /** Synchronously perform database operations in pool using transaction. 146 | 147 | @param block The code to be run on the `FMDatabasePool` pool. 148 | */ 149 | 150 | - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; 151 | 152 | /** Synchronously perform database operations in pool using deferred transaction. 153 | 154 | @param block The code to be run on the `FMDatabasePool` pool. 155 | */ 156 | 157 | - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; 158 | 159 | #if SQLITE_VERSION_NUMBER >= 3007000 160 | 161 | /** Synchronously perform database operations in pool using save point. 162 | 163 | @param block The code to be run on the `FMDatabasePool` pool. 164 | 165 | @return `NSError` object if error; `nil` if successful. 166 | 167 | @warning You can not nest these, since calling it will pull another database out of the pool and you'll get a deadlock. If you need to nest, use `<[FMDatabase startSavePointWithName:error:]>` instead. 168 | */ 169 | 170 | - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block; 171 | #endif 172 | 173 | @end 174 | 175 | 176 | /** FMDatabasePool delegate category 177 | 178 | This is a category that defines the protocol for the FMDatabasePool delegate 179 | */ 180 | 181 | @interface NSObject (FMDatabasePoolDelegate) 182 | 183 | /** Asks the delegate whether database should be added to the pool. 184 | 185 | @param pool The `FMDatabasePool` object. 186 | @param database The `FMDatabase` object. 187 | 188 | @return `YES` if it should add database to pool; `NO` if not. 189 | 190 | */ 191 | 192 | - (BOOL)databasePool:(FMDatabasePool*)pool shouldAddDatabaseToPool:(FMDatabase*)database; 193 | 194 | /** Tells the delegate that database was added to the pool. 195 | 196 | @param pool The `FMDatabasePool` object. 197 | @param database The `FMDatabase` object. 198 | 199 | */ 200 | 201 | - (void)databasePool:(FMDatabasePool*)pool didAddDatabase:(FMDatabase*)database; 202 | 203 | @end 204 | 205 | -------------------------------------------------------------------------------- /JKDBModel/FMDB/FMDatabasePool.m: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabasePool.m 3 | // fmdb 4 | // 5 | // Created by August Mueller on 6/22/11. 6 | // Copyright 2011 Flying Meat Inc. All rights reserved. 7 | // 8 | 9 | #import "FMDatabasePool.h" 10 | #import "FMDatabase.h" 11 | 12 | @interface FMDatabasePool() 13 | 14 | - (void)pushDatabaseBackInPool:(FMDatabase*)db; 15 | - (FMDatabase*)db; 16 | 17 | @end 18 | 19 | 20 | @implementation FMDatabasePool 21 | @synthesize path=_path; 22 | @synthesize delegate=_delegate; 23 | @synthesize maximumNumberOfDatabasesToCreate=_maximumNumberOfDatabasesToCreate; 24 | @synthesize openFlags=_openFlags; 25 | 26 | 27 | + (instancetype)databasePoolWithPath:(NSString*)aPath { 28 | return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]); 29 | } 30 | 31 | + (instancetype)databasePoolWithPath:(NSString*)aPath flags:(int)openFlags { 32 | return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath flags:openFlags]); 33 | } 34 | 35 | - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags { 36 | 37 | self = [super init]; 38 | 39 | if (self != nil) { 40 | _path = [aPath copy]; 41 | _lockQueue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL); 42 | _databaseInPool = FMDBReturnRetained([NSMutableArray array]); 43 | _databaseOutPool = FMDBReturnRetained([NSMutableArray array]); 44 | _openFlags = openFlags; 45 | } 46 | 47 | return self; 48 | } 49 | 50 | - (instancetype)initWithPath:(NSString*)aPath 51 | { 52 | // default flags for sqlite3_open 53 | return [self initWithPath:aPath flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE]; 54 | } 55 | 56 | - (instancetype)init { 57 | return [self initWithPath:nil]; 58 | } 59 | 60 | 61 | - (void)dealloc { 62 | 63 | _delegate = 0x00; 64 | FMDBRelease(_path); 65 | FMDBRelease(_databaseInPool); 66 | FMDBRelease(_databaseOutPool); 67 | 68 | if (_lockQueue) { 69 | FMDBDispatchQueueRelease(_lockQueue); 70 | _lockQueue = 0x00; 71 | } 72 | #if ! __has_feature(objc_arc) 73 | [super dealloc]; 74 | #endif 75 | } 76 | 77 | 78 | - (void)executeLocked:(void (^)(void))aBlock { 79 | dispatch_sync(_lockQueue, aBlock); 80 | } 81 | 82 | - (void)pushDatabaseBackInPool:(FMDatabase*)db { 83 | 84 | if (!db) { // db can be null if we set an upper bound on the # of databases to create. 85 | return; 86 | } 87 | 88 | [self executeLocked:^() { 89 | 90 | if ([self->_databaseInPool containsObject:db]) { 91 | [[NSException exceptionWithName:@"Database already in pool" reason:@"The FMDatabase being put back into the pool is already present in the pool" userInfo:nil] raise]; 92 | } 93 | 94 | [self->_databaseInPool addObject:db]; 95 | [self->_databaseOutPool removeObject:db]; 96 | 97 | }]; 98 | } 99 | 100 | - (FMDatabase*)db { 101 | 102 | __block FMDatabase *db; 103 | 104 | 105 | [self executeLocked:^() { 106 | db = [self->_databaseInPool lastObject]; 107 | 108 | BOOL shouldNotifyDelegate = NO; 109 | 110 | if (db) { 111 | [self->_databaseOutPool addObject:db]; 112 | [self->_databaseInPool removeLastObject]; 113 | } 114 | else { 115 | 116 | if (self->_maximumNumberOfDatabasesToCreate) { 117 | NSUInteger currentCount = [self->_databaseOutPool count] + [self->_databaseInPool count]; 118 | 119 | if (currentCount >= self->_maximumNumberOfDatabasesToCreate) { 120 | NSLog(@"Maximum number of databases (%ld) has already been reached!", (long)currentCount); 121 | return; 122 | } 123 | } 124 | 125 | db = [FMDatabase databaseWithPath:self->_path]; 126 | shouldNotifyDelegate = YES; 127 | } 128 | 129 | //This ensures that the db is opened before returning 130 | #if SQLITE_VERSION_NUMBER >= 3005000 131 | BOOL success = [db openWithFlags:self->_openFlags]; 132 | #else 133 | BOOL success = [db open]; 134 | #endif 135 | if (success) { 136 | if ([self->_delegate respondsToSelector:@selector(databasePool:shouldAddDatabaseToPool:)] && ![self->_delegate databasePool:self shouldAddDatabaseToPool:db]) { 137 | [db close]; 138 | db = 0x00; 139 | } 140 | else { 141 | //It should not get added in the pool twice if lastObject was found 142 | if (![self->_databaseOutPool containsObject:db]) { 143 | [self->_databaseOutPool addObject:db]; 144 | 145 | if (shouldNotifyDelegate && [self->_delegate respondsToSelector:@selector(databasePool:didAddDatabase:)]) { 146 | [self->_delegate databasePool:self didAddDatabase:db]; 147 | } 148 | } 149 | } 150 | } 151 | else { 152 | NSLog(@"Could not open up the database at path %@", self->_path); 153 | db = 0x00; 154 | } 155 | }]; 156 | 157 | return db; 158 | } 159 | 160 | - (NSUInteger)countOfCheckedInDatabases { 161 | 162 | __block NSUInteger count; 163 | 164 | [self executeLocked:^() { 165 | count = [self->_databaseInPool count]; 166 | }]; 167 | 168 | return count; 169 | } 170 | 171 | - (NSUInteger)countOfCheckedOutDatabases { 172 | 173 | __block NSUInteger count; 174 | 175 | [self executeLocked:^() { 176 | count = [self->_databaseOutPool count]; 177 | }]; 178 | 179 | return count; 180 | } 181 | 182 | - (NSUInteger)countOfOpenDatabases { 183 | __block NSUInteger count; 184 | 185 | [self executeLocked:^() { 186 | count = [self->_databaseOutPool count] + [self->_databaseInPool count]; 187 | }]; 188 | 189 | return count; 190 | } 191 | 192 | - (void)releaseAllDatabases { 193 | [self executeLocked:^() { 194 | [self->_databaseOutPool removeAllObjects]; 195 | [self->_databaseInPool removeAllObjects]; 196 | }]; 197 | } 198 | 199 | - (void)inDatabase:(void (^)(FMDatabase *db))block { 200 | 201 | FMDatabase *db = [self db]; 202 | 203 | block(db); 204 | 205 | [self pushDatabaseBackInPool:db]; 206 | } 207 | 208 | - (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block { 209 | 210 | BOOL shouldRollback = NO; 211 | 212 | FMDatabase *db = [self db]; 213 | 214 | if (useDeferred) { 215 | [db beginDeferredTransaction]; 216 | } 217 | else { 218 | [db beginTransaction]; 219 | } 220 | 221 | 222 | block(db, &shouldRollback); 223 | 224 | if (shouldRollback) { 225 | [db rollback]; 226 | } 227 | else { 228 | [db commit]; 229 | } 230 | 231 | [self pushDatabaseBackInPool:db]; 232 | } 233 | 234 | - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { 235 | [self beginTransaction:YES withBlock:block]; 236 | } 237 | 238 | - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { 239 | [self beginTransaction:NO withBlock:block]; 240 | } 241 | #if SQLITE_VERSION_NUMBER >= 3007000 242 | - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block { 243 | 244 | static unsigned long savePointIdx = 0; 245 | 246 | NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++]; 247 | 248 | BOOL shouldRollback = NO; 249 | 250 | FMDatabase *db = [self db]; 251 | 252 | NSError *err = 0x00; 253 | 254 | if (![db startSavePointWithName:name error:&err]) { 255 | [self pushDatabaseBackInPool:db]; 256 | return err; 257 | } 258 | 259 | block(db, &shouldRollback); 260 | 261 | if (shouldRollback) { 262 | // We need to rollback and release this savepoint to remove it 263 | [db rollbackToSavePointWithName:name error:&err]; 264 | } 265 | [db releaseSavePointWithName:name error:&err]; 266 | 267 | [self pushDatabaseBackInPool:db]; 268 | 269 | return err; 270 | } 271 | #endif 272 | 273 | @end 274 | -------------------------------------------------------------------------------- /JKDBModel/FMDB/FMDatabaseQueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabaseQueue.h 3 | // fmdb 4 | // 5 | // Created by August Mueller on 6/22/11. 6 | // Copyright 2011 Flying Meat Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "sqlite3.h" 11 | 12 | @class FMDatabase; 13 | 14 | /** To perform queries and updates on multiple threads, you'll want to use `FMDatabaseQueue`. 15 | 16 | Using a single instance of `` from multiple threads at once is a bad idea. It has always been OK to make a `` object *per thread*. Just don't share a single instance across threads, and definitely not across multiple threads at the same time. 17 | 18 | Instead, use `FMDatabaseQueue`. Here's how to use it: 19 | 20 | First, make your queue. 21 | 22 | FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath]; 23 | 24 | Then use it like so: 25 | 26 | [queue inDatabase:^(FMDatabase *db) { 27 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]]; 28 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]]; 29 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]]; 30 | 31 | FMResultSet *rs = [db executeQuery:@"select * from foo"]; 32 | while ([rs next]) { 33 | //… 34 | } 35 | }]; 36 | 37 | An easy way to wrap things up in a transaction can be done like this: 38 | 39 | [queue inTransaction:^(FMDatabase *db, BOOL *rollback) { 40 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]]; 41 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]]; 42 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]]; 43 | 44 | if (whoopsSomethingWrongHappened) { 45 | *rollback = YES; 46 | return; 47 | } 48 | // etc… 49 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]]; 50 | }]; 51 | 52 | `FMDatabaseQueue` will run the blocks on a serialized queue (hence the name of the class). So if you call `FMDatabaseQueue`'s methods from multiple threads at the same time, they will be executed in the order they are received. This way queries and updates won't step on each other's toes, and every one is happy. 53 | 54 | ### See also 55 | 56 | - `` 57 | 58 | @warning Do not instantiate a single `` object and use it across multiple threads. Use `FMDatabaseQueue` instead. 59 | 60 | @warning The calls to `FMDatabaseQueue`'s methods are blocking. So even though you are passing along blocks, they will **not** be run on another thread. 61 | 62 | */ 63 | 64 | @interface FMDatabaseQueue : NSObject { 65 | NSString *_path; 66 | dispatch_queue_t _queue; 67 | FMDatabase *_db; 68 | int _openFlags; 69 | } 70 | 71 | /** Path of database */ 72 | 73 | @property (atomic, retain) NSString *path; 74 | 75 | /** Open flags */ 76 | 77 | @property (atomic, readonly) int openFlags; 78 | 79 | ///---------------------------------------------------- 80 | /// @name Initialization, opening, and closing of queue 81 | ///---------------------------------------------------- 82 | 83 | /** Create queue using path. 84 | 85 | @param aPath The file path of the database. 86 | 87 | @return The `FMDatabaseQueue` object. `nil` on error. 88 | */ 89 | 90 | + (instancetype)databaseQueueWithPath:(NSString*)aPath; 91 | 92 | /** Create queue using path and specified flags. 93 | 94 | @param aPath The file path of the database. 95 | @param openFlags Flags passed to the openWithFlags method of the database 96 | 97 | @return The `FMDatabaseQueue` object. `nil` on error. 98 | */ 99 | + (instancetype)databaseQueueWithPath:(NSString*)aPath flags:(int)openFlags; 100 | 101 | /** Create queue using path. 102 | 103 | @param aPath The file path of the database. 104 | 105 | @return The `FMDatabaseQueue` object. `nil` on error. 106 | */ 107 | 108 | - (instancetype)initWithPath:(NSString*)aPath; 109 | 110 | /** Create queue using path and specified flags. 111 | 112 | @param aPath The file path of the database. 113 | @param openFlags Flags passed to the openWithFlags method of the database 114 | 115 | @return The `FMDatabaseQueue` object. `nil` on error. 116 | */ 117 | 118 | - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags; 119 | 120 | /** Returns the Class of 'FMDatabase' subclass, that will be used to instantiate database object. 121 | 122 | Subclasses can override this method to return specified Class of 'FMDatabase' subclass. 123 | 124 | @return The Class of 'FMDatabase' subclass, that will be used to instantiate database object. 125 | */ 126 | 127 | + (Class)databaseClass; 128 | 129 | /** Close database used by queue. */ 130 | 131 | - (void)close; 132 | 133 | ///----------------------------------------------- 134 | /// @name Dispatching database operations to queue 135 | ///----------------------------------------------- 136 | 137 | /** Synchronously perform database operations on queue. 138 | 139 | @param block The code to be run on the queue of `FMDatabaseQueue` 140 | */ 141 | 142 | - (void)inDatabase:(void (^)(FMDatabase *db))block; 143 | 144 | /** Synchronously perform database operations on queue, using transactions. 145 | 146 | @param block The code to be run on the queue of `FMDatabaseQueue` 147 | */ 148 | 149 | - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; 150 | 151 | /** Synchronously perform database operations on queue, using deferred transactions. 152 | 153 | @param block The code to be run on the queue of `FMDatabaseQueue` 154 | */ 155 | 156 | - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; 157 | 158 | ///----------------------------------------------- 159 | /// @name Dispatching database operations to queue 160 | ///----------------------------------------------- 161 | 162 | /** Synchronously perform database operations using save point. 163 | 164 | @param block The code to be run on the queue of `FMDatabaseQueue` 165 | */ 166 | 167 | #if SQLITE_VERSION_NUMBER >= 3007000 168 | // NOTE: you can not nest these, since calling it will pull another database out of the pool and you'll get a deadlock. 169 | // If you need to nest, use FMDatabase's startSavePointWithName:error: instead. 170 | - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block; 171 | #endif 172 | 173 | @end 174 | 175 | -------------------------------------------------------------------------------- /JKDBModel/FMDB/FMDatabaseQueue.m: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabaseQueue.m 3 | // fmdb 4 | // 5 | // Created by August Mueller on 6/22/11. 6 | // Copyright 2011 Flying Meat Inc. All rights reserved. 7 | // 8 | 9 | #import "FMDatabaseQueue.h" 10 | #import "FMDatabase.h" 11 | 12 | /* 13 | 14 | Note: we call [self retain]; before using dispatch_sync, just incase 15 | FMDatabaseQueue is released on another thread and we're in the middle of doing 16 | something in dispatch_sync 17 | 18 | */ 19 | 20 | /* 21 | * A key used to associate the FMDatabaseQueue object with the dispatch_queue_t it uses. 22 | * This in turn is used for deadlock detection by seeing if inDatabase: is called on 23 | * the queue's dispatch queue, which should not happen and causes a deadlock. 24 | */ 25 | static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey; 26 | 27 | @implementation FMDatabaseQueue 28 | 29 | @synthesize path = _path; 30 | @synthesize openFlags = _openFlags; 31 | 32 | + (instancetype)databaseQueueWithPath:(NSString*)aPath { 33 | 34 | FMDatabaseQueue *q = [[self alloc] initWithPath:aPath]; 35 | 36 | FMDBAutorelease(q); 37 | 38 | return q; 39 | } 40 | 41 | + (instancetype)databaseQueueWithPath:(NSString*)aPath flags:(int)openFlags { 42 | 43 | FMDatabaseQueue *q = [[self alloc] initWithPath:aPath flags:openFlags]; 44 | 45 | FMDBAutorelease(q); 46 | 47 | return q; 48 | } 49 | 50 | + (Class)databaseClass { 51 | return [FMDatabase class]; 52 | } 53 | 54 | - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags { 55 | 56 | self = [super init]; 57 | 58 | if (self != nil) { 59 | 60 | _db = [[[self class] databaseClass] databaseWithPath:aPath]; 61 | FMDBRetain(_db); 62 | 63 | #if SQLITE_VERSION_NUMBER >= 3005000 64 | BOOL success = [_db openWithFlags:openFlags]; 65 | #else 66 | BOOL success = [_db open]; 67 | #endif 68 | if (!success) { 69 | NSLog(@"Could not create database queue for path %@", aPath); 70 | FMDBRelease(self); 71 | return 0x00; 72 | } 73 | 74 | _path = FMDBReturnRetained(aPath); 75 | 76 | _queue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL); 77 | dispatch_queue_set_specific(_queue, kDispatchQueueSpecificKey, (__bridge void *)self, NULL); 78 | _openFlags = openFlags; 79 | } 80 | 81 | return self; 82 | } 83 | 84 | - (instancetype)initWithPath:(NSString*)aPath { 85 | 86 | // default flags for sqlite3_open 87 | return [self initWithPath:aPath flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE]; 88 | } 89 | 90 | - (instancetype)init { 91 | return [self initWithPath:nil]; 92 | } 93 | 94 | 95 | - (void)dealloc { 96 | 97 | FMDBRelease(_db); 98 | FMDBRelease(_path); 99 | 100 | if (_queue) { 101 | FMDBDispatchQueueRelease(_queue); 102 | _queue = 0x00; 103 | } 104 | #if ! __has_feature(objc_arc) 105 | [super dealloc]; 106 | #endif 107 | } 108 | 109 | - (void)close { 110 | FMDBRetain(self); 111 | dispatch_sync(_queue, ^() { 112 | [self->_db close]; 113 | FMDBRelease(_db); 114 | self->_db = 0x00; 115 | }); 116 | FMDBRelease(self); 117 | } 118 | 119 | - (FMDatabase*)database { 120 | if (!_db) { 121 | _db = FMDBReturnRetained([FMDatabase databaseWithPath:_path]); 122 | 123 | #if SQLITE_VERSION_NUMBER >= 3005000 124 | BOOL success = [_db openWithFlags:_openFlags]; 125 | #else 126 | BOOL success = [_db open]; 127 | #endif 128 | if (!success) { 129 | NSLog(@"FMDatabaseQueue could not reopen database for path %@", _path); 130 | FMDBRelease(_db); 131 | _db = 0x00; 132 | return 0x00; 133 | } 134 | } 135 | 136 | return _db; 137 | } 138 | 139 | - (void)inDatabase:(void (^)(FMDatabase *db))block { 140 | /* Get the currently executing queue (which should probably be nil, but in theory could be another DB queue 141 | * and then check it against self to make sure we're not about to deadlock. */ 142 | FMDatabaseQueue *currentSyncQueue = (__bridge id)dispatch_get_specific(kDispatchQueueSpecificKey); 143 | assert(currentSyncQueue != self && "inDatabase: was called reentrantly on the same queue, which would lead to a deadlock"); 144 | 145 | FMDBRetain(self); 146 | 147 | dispatch_sync(_queue, ^() { 148 | 149 | FMDatabase *db = [self database]; 150 | block(db); 151 | 152 | if ([db hasOpenResultSets]) { 153 | NSLog(@"Warning: there is at least one open result set around after performing [FMDatabaseQueue inDatabase:]"); 154 | 155 | #if defined(DEBUG) && DEBUG 156 | NSSet *openSetCopy = FMDBReturnAutoreleased([[db valueForKey:@"_openResultSets"] copy]); 157 | for (NSValue *rsInWrappedInATastyValueMeal in openSetCopy) { 158 | FMResultSet *rs = (FMResultSet *)[rsInWrappedInATastyValueMeal pointerValue]; 159 | NSLog(@"query: '%@'", [rs query]); 160 | } 161 | #endif 162 | } 163 | }); 164 | 165 | FMDBRelease(self); 166 | } 167 | 168 | 169 | - (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block { 170 | FMDBRetain(self); 171 | dispatch_sync(_queue, ^() { 172 | 173 | BOOL shouldRollback = NO; 174 | 175 | if (useDeferred) { 176 | [[self database] beginDeferredTransaction]; 177 | } 178 | else { 179 | [[self database] beginTransaction]; 180 | } 181 | 182 | block([self database], &shouldRollback); 183 | 184 | if (shouldRollback) { 185 | [[self database] rollback]; 186 | } 187 | else { 188 | [[self database] commit]; 189 | } 190 | }); 191 | 192 | FMDBRelease(self); 193 | } 194 | 195 | - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { 196 | [self beginTransaction:YES withBlock:block]; 197 | } 198 | 199 | - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { 200 | [self beginTransaction:NO withBlock:block]; 201 | } 202 | 203 | #if SQLITE_VERSION_NUMBER >= 3007000 204 | - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block { 205 | 206 | static unsigned long savePointIdx = 0; 207 | __block NSError *err = 0x00; 208 | FMDBRetain(self); 209 | dispatch_sync(_queue, ^() { 210 | 211 | NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++]; 212 | 213 | BOOL shouldRollback = NO; 214 | 215 | if ([[self database] startSavePointWithName:name error:&err]) { 216 | 217 | block([self database], &shouldRollback); 218 | 219 | if (shouldRollback) { 220 | // We need to rollback and release this savepoint to remove it 221 | [[self database] rollbackToSavePointWithName:name error:&err]; 222 | } 223 | [[self database] releaseSavePointWithName:name error:&err]; 224 | 225 | } 226 | }); 227 | FMDBRelease(self); 228 | return err; 229 | } 230 | #endif 231 | 232 | @end 233 | -------------------------------------------------------------------------------- /JKDBModel/FMDB/FMResultSet.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "sqlite3.h" 3 | 4 | #ifndef __has_feature // Optional. 5 | #define __has_feature(x) 0 // Compatibility with non-clang compilers. 6 | #endif 7 | 8 | #ifndef NS_RETURNS_NOT_RETAINED 9 | #if __has_feature(attribute_ns_returns_not_retained) 10 | #define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained)) 11 | #else 12 | #define NS_RETURNS_NOT_RETAINED 13 | #endif 14 | #endif 15 | 16 | @class FMDatabase; 17 | @class FMStatement; 18 | 19 | /** Represents the results of executing a query on an ``. 20 | 21 | ### See also 22 | 23 | - `` 24 | */ 25 | 26 | @interface FMResultSet : NSObject { 27 | FMDatabase *_parentDB; 28 | FMStatement *_statement; 29 | 30 | NSString *_query; 31 | NSMutableDictionary *_columnNameToIndexMap; 32 | } 33 | 34 | ///----------------- 35 | /// @name Properties 36 | ///----------------- 37 | 38 | /** Executed query */ 39 | 40 | @property (atomic, retain) NSString *query; 41 | 42 | /** `NSMutableDictionary` mapping column names to numeric index */ 43 | 44 | @property (readonly) NSMutableDictionary *columnNameToIndexMap; 45 | 46 | /** `FMStatement` used by result set. */ 47 | 48 | @property (atomic, retain) FMStatement *statement; 49 | 50 | ///------------------------------------ 51 | /// @name Creating and closing database 52 | ///------------------------------------ 53 | 54 | /** Create result set from `` 55 | 56 | @param statement A `` to be performed 57 | 58 | @param aDB A `` to be used 59 | 60 | @return A `FMResultSet` on success; `nil` on failure 61 | */ 62 | 63 | + (instancetype)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB; 64 | 65 | /** Close result set */ 66 | 67 | - (void)close; 68 | 69 | - (void)setParentDB:(FMDatabase *)newDb; 70 | 71 | ///--------------------------------------- 72 | /// @name Iterating through the result set 73 | ///--------------------------------------- 74 | 75 | /** Retrieve next row for result set. 76 | 77 | You must always invoke `next` or `nextWithError` before attempting to access the values returned in a query, even if you're only expecting one. 78 | 79 | @return `YES` if row successfully retrieved; `NO` if end of result set reached 80 | 81 | @see hasAnotherRow 82 | */ 83 | 84 | - (BOOL)next; 85 | 86 | /** Retrieve next row for result set. 87 | 88 | You must always invoke `next` or `nextWithError` before attempting to access the values returned in a query, even if you're only expecting one. 89 | 90 | @param outErr A 'NSError' object to receive any error object (if any). 91 | 92 | @return 'YES' if row successfully retrieved; 'NO' if end of result set reached 93 | 94 | @see hasAnotherRow 95 | */ 96 | 97 | - (BOOL)nextWithError:(NSError **)outErr; 98 | 99 | /** Did the last call to `` succeed in retrieving another row? 100 | 101 | @return `YES` if the last call to `` succeeded in retrieving another record; `NO` if not. 102 | 103 | @see next 104 | 105 | @warning The `hasAnotherRow` method must follow a call to ``. If the previous database interaction was something other than a call to `next`, then this method may return `NO`, whether there is another row of data or not. 106 | */ 107 | 108 | - (BOOL)hasAnotherRow; 109 | 110 | ///--------------------------------------------- 111 | /// @name Retrieving information from result set 112 | ///--------------------------------------------- 113 | 114 | /** How many columns in result set 115 | 116 | @return Integer value of the number of columns. 117 | */ 118 | 119 | - (int)columnCount; 120 | 121 | /** Column index for column name 122 | 123 | @param columnName `NSString` value of the name of the column. 124 | 125 | @return Zero-based index for column. 126 | */ 127 | 128 | - (int)columnIndexForName:(NSString*)columnName; 129 | 130 | /** Column name for column index 131 | 132 | @param columnIdx Zero-based index for column. 133 | 134 | @return columnName `NSString` value of the name of the column. 135 | */ 136 | 137 | - (NSString*)columnNameForIndex:(int)columnIdx; 138 | 139 | /** Result set integer value for column. 140 | 141 | @param columnName `NSString` value of the name of the column. 142 | 143 | @return `int` value of the result set's column. 144 | */ 145 | 146 | - (int)intForColumn:(NSString*)columnName; 147 | 148 | /** Result set integer value for column. 149 | 150 | @param columnIdx Zero-based index for column. 151 | 152 | @return `int` value of the result set's column. 153 | */ 154 | 155 | - (int)intForColumnIndex:(int)columnIdx; 156 | 157 | /** Result set `long` value for column. 158 | 159 | @param columnName `NSString` value of the name of the column. 160 | 161 | @return `long` value of the result set's column. 162 | */ 163 | 164 | - (long)longForColumn:(NSString*)columnName; 165 | 166 | /** Result set long value for column. 167 | 168 | @param columnIdx Zero-based index for column. 169 | 170 | @return `long` value of the result set's column. 171 | */ 172 | 173 | - (long)longForColumnIndex:(int)columnIdx; 174 | 175 | /** Result set `long long int` value for column. 176 | 177 | @param columnName `NSString` value of the name of the column. 178 | 179 | @return `long long int` value of the result set's column. 180 | */ 181 | 182 | - (long long int)longLongIntForColumn:(NSString*)columnName; 183 | 184 | /** Result set `long long int` value for column. 185 | 186 | @param columnIdx Zero-based index for column. 187 | 188 | @return `long long int` value of the result set's column. 189 | */ 190 | 191 | - (long long int)longLongIntForColumnIndex:(int)columnIdx; 192 | 193 | /** Result set `unsigned long long int` value for column. 194 | 195 | @param columnName `NSString` value of the name of the column. 196 | 197 | @return `unsigned long long int` value of the result set's column. 198 | */ 199 | 200 | - (unsigned long long int)unsignedLongLongIntForColumn:(NSString*)columnName; 201 | 202 | /** Result set `unsigned long long int` value for column. 203 | 204 | @param columnIdx Zero-based index for column. 205 | 206 | @return `unsigned long long int` value of the result set's column. 207 | */ 208 | 209 | - (unsigned long long int)unsignedLongLongIntForColumnIndex:(int)columnIdx; 210 | 211 | /** Result set `BOOL` value for column. 212 | 213 | @param columnName `NSString` value of the name of the column. 214 | 215 | @return `BOOL` value of the result set's column. 216 | */ 217 | 218 | - (BOOL)boolForColumn:(NSString*)columnName; 219 | 220 | /** Result set `BOOL` value for column. 221 | 222 | @param columnIdx Zero-based index for column. 223 | 224 | @return `BOOL` value of the result set's column. 225 | */ 226 | 227 | - (BOOL)boolForColumnIndex:(int)columnIdx; 228 | 229 | /** Result set `double` value for column. 230 | 231 | @param columnName `NSString` value of the name of the column. 232 | 233 | @return `double` value of the result set's column. 234 | 235 | */ 236 | 237 | - (double)doubleForColumn:(NSString*)columnName; 238 | 239 | /** Result set `double` value for column. 240 | 241 | @param columnIdx Zero-based index for column. 242 | 243 | @return `double` value of the result set's column. 244 | 245 | */ 246 | 247 | - (double)doubleForColumnIndex:(int)columnIdx; 248 | 249 | /** Result set `NSString` value for column. 250 | 251 | @param columnName `NSString` value of the name of the column. 252 | 253 | @return `NSString` value of the result set's column. 254 | 255 | */ 256 | 257 | - (NSString*)stringForColumn:(NSString*)columnName; 258 | 259 | /** Result set `NSString` value for column. 260 | 261 | @param columnIdx Zero-based index for column. 262 | 263 | @return `NSString` value of the result set's column. 264 | */ 265 | 266 | - (NSString*)stringForColumnIndex:(int)columnIdx; 267 | 268 | /** Result set `NSDate` value for column. 269 | 270 | @param columnName `NSString` value of the name of the column. 271 | 272 | @return `NSDate` value of the result set's column. 273 | */ 274 | 275 | - (NSDate*)dateForColumn:(NSString*)columnName; 276 | 277 | /** Result set `NSDate` value for column. 278 | 279 | @param columnIdx Zero-based index for column. 280 | 281 | @return `NSDate` value of the result set's column. 282 | 283 | */ 284 | 285 | - (NSDate*)dateForColumnIndex:(int)columnIdx; 286 | 287 | /** Result set `NSData` value for column. 288 | 289 | This is useful when storing binary data in table (such as image or the like). 290 | 291 | @param columnName `NSString` value of the name of the column. 292 | 293 | @return `NSData` value of the result set's column. 294 | 295 | */ 296 | 297 | - (NSData*)dataForColumn:(NSString*)columnName; 298 | 299 | /** Result set `NSData` value for column. 300 | 301 | @param columnIdx Zero-based index for column. 302 | 303 | @return `NSData` value of the result set's column. 304 | */ 305 | 306 | - (NSData*)dataForColumnIndex:(int)columnIdx; 307 | 308 | /** Result set `(const unsigned char *)` value for column. 309 | 310 | @param columnName `NSString` value of the name of the column. 311 | 312 | @return `(const unsigned char *)` value of the result set's column. 313 | */ 314 | 315 | - (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName; 316 | 317 | /** Result set `(const unsigned char *)` value for column. 318 | 319 | @param columnIdx Zero-based index for column. 320 | 321 | @return `(const unsigned char *)` value of the result set's column. 322 | */ 323 | 324 | - (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx; 325 | 326 | /** Result set object for column. 327 | 328 | @param columnName `NSString` value of the name of the column. 329 | 330 | @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. 331 | 332 | @see objectForKeyedSubscript: 333 | */ 334 | 335 | - (id)objectForColumnName:(NSString*)columnName; 336 | 337 | /** Result set object for column. 338 | 339 | @param columnIdx Zero-based index for column. 340 | 341 | @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. 342 | 343 | @see objectAtIndexedSubscript: 344 | */ 345 | 346 | - (id)objectForColumnIndex:(int)columnIdx; 347 | 348 | /** Result set object for column. 349 | 350 | This method allows the use of the "boxed" syntax supported in Modern Objective-C. For example, by defining this method, the following syntax is now supported: 351 | 352 | id result = rs[@"employee_name"]; 353 | 354 | This simplified syntax is equivalent to calling: 355 | 356 | id result = [rs objectForKeyedSubscript:@"employee_name"]; 357 | 358 | which is, it turns out, equivalent to calling: 359 | 360 | id result = [rs objectForColumnName:@"employee_name"]; 361 | 362 | @param columnName `NSString` value of the name of the column. 363 | 364 | @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. 365 | */ 366 | 367 | - (id)objectForKeyedSubscript:(NSString *)columnName; 368 | 369 | /** Result set object for column. 370 | 371 | This method allows the use of the "boxed" syntax supported in Modern Objective-C. For example, by defining this method, the following syntax is now supported: 372 | 373 | id result = rs[0]; 374 | 375 | This simplified syntax is equivalent to calling: 376 | 377 | id result = [rs objectForKeyedSubscript:0]; 378 | 379 | which is, it turns out, equivalent to calling: 380 | 381 | id result = [rs objectForColumnName:0]; 382 | 383 | @param columnIdx Zero-based index for column. 384 | 385 | @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. 386 | */ 387 | 388 | - (id)objectAtIndexedSubscript:(int)columnIdx; 389 | 390 | /** Result set `NSData` value for column. 391 | 392 | @param columnName `NSString` value of the name of the column. 393 | 394 | @return `NSData` value of the result set's column. 395 | 396 | @warning If you are going to use this data after you iterate over the next row, or after you close the 397 | result set, make sure to make a copy of the data first (or just use ``/``) 398 | If you don't, you're going to be in a world of hurt when you try and use the data. 399 | 400 | */ 401 | 402 | - (NSData*)dataNoCopyForColumn:(NSString*)columnName NS_RETURNS_NOT_RETAINED; 403 | 404 | /** Result set `NSData` value for column. 405 | 406 | @param columnIdx Zero-based index for column. 407 | 408 | @return `NSData` value of the result set's column. 409 | 410 | @warning If you are going to use this data after you iterate over the next row, or after you close the 411 | result set, make sure to make a copy of the data first (or just use ``/``) 412 | If you don't, you're going to be in a world of hurt when you try and use the data. 413 | 414 | */ 415 | 416 | - (NSData*)dataNoCopyForColumnIndex:(int)columnIdx NS_RETURNS_NOT_RETAINED; 417 | 418 | /** Is the column `NULL`? 419 | 420 | @param columnIdx Zero-based index for column. 421 | 422 | @return `YES` if column is `NULL`; `NO` if not `NULL`. 423 | */ 424 | 425 | - (BOOL)columnIndexIsNull:(int)columnIdx; 426 | 427 | /** Is the column `NULL`? 428 | 429 | @param columnName `NSString` value of the name of the column. 430 | 431 | @return `YES` if column is `NULL`; `NO` if not `NULL`. 432 | */ 433 | 434 | - (BOOL)columnIsNull:(NSString*)columnName; 435 | 436 | 437 | /** Returns a dictionary of the row results mapped to case sensitive keys of the column names. 438 | 439 | @returns `NSDictionary` of the row results. 440 | 441 | @warning The keys to the dictionary are case sensitive of the column names. 442 | */ 443 | 444 | - (NSDictionary*)resultDictionary; 445 | 446 | /** Returns a dictionary of the row results 447 | 448 | @see resultDictionary 449 | 450 | @warning **Deprecated**: Please use `` instead. Also, beware that `` is case sensitive! 451 | */ 452 | 453 | - (NSDictionary*)resultDict __attribute__ ((deprecated)); 454 | 455 | ///----------------------------- 456 | /// @name Key value coding magic 457 | ///----------------------------- 458 | 459 | /** Performs `setValue` to yield support for key value observing. 460 | 461 | @param object The object for which the values will be set. This is the key-value-coding compliant object that you might, for example, observe. 462 | 463 | */ 464 | 465 | - (void)kvcMagic:(id)object; 466 | 467 | 468 | @end 469 | 470 | -------------------------------------------------------------------------------- /JKDBModel/FMDB/FMResultSet.m: -------------------------------------------------------------------------------- 1 | #import "FMResultSet.h" 2 | #import "FMDatabase.h" 3 | #import "unistd.h" 4 | 5 | @interface FMDatabase () 6 | - (void)resultSetDidClose:(FMResultSet *)resultSet; 7 | @end 8 | 9 | 10 | @implementation FMResultSet 11 | @synthesize query=_query; 12 | @synthesize statement=_statement; 13 | 14 | + (instancetype)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB { 15 | 16 | FMResultSet *rs = [[FMResultSet alloc] init]; 17 | 18 | [rs setStatement:statement]; 19 | [rs setParentDB:aDB]; 20 | 21 | NSParameterAssert(![statement inUse]); 22 | [statement setInUse:YES]; // weak reference 23 | 24 | return FMDBReturnAutoreleased(rs); 25 | } 26 | 27 | - (void)finalize { 28 | [self close]; 29 | [super finalize]; 30 | } 31 | 32 | - (void)dealloc { 33 | [self close]; 34 | 35 | FMDBRelease(_query); 36 | _query = nil; 37 | 38 | FMDBRelease(_columnNameToIndexMap); 39 | _columnNameToIndexMap = nil; 40 | 41 | #if ! __has_feature(objc_arc) 42 | [super dealloc]; 43 | #endif 44 | } 45 | 46 | - (void)close { 47 | [_statement reset]; 48 | FMDBRelease(_statement); 49 | _statement = nil; 50 | 51 | // we don't need this anymore... (i think) 52 | //[_parentDB setInUse:NO]; 53 | [_parentDB resultSetDidClose:self]; 54 | _parentDB = nil; 55 | } 56 | 57 | - (int)columnCount { 58 | return sqlite3_column_count([_statement statement]); 59 | } 60 | 61 | - (NSMutableDictionary *)columnNameToIndexMap { 62 | if (!_columnNameToIndexMap) { 63 | int columnCount = sqlite3_column_count([_statement statement]); 64 | _columnNameToIndexMap = [[NSMutableDictionary alloc] initWithCapacity:(NSUInteger)columnCount]; 65 | int columnIdx = 0; 66 | for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { 67 | [_columnNameToIndexMap setObject:[NSNumber numberWithInt:columnIdx] 68 | forKey:[[NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)] lowercaseString]]; 69 | } 70 | } 71 | return _columnNameToIndexMap; 72 | } 73 | 74 | - (void)kvcMagic:(id)object { 75 | 76 | int columnCount = sqlite3_column_count([_statement statement]); 77 | 78 | int columnIdx = 0; 79 | for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { 80 | 81 | const char *c = (const char *)sqlite3_column_text([_statement statement], columnIdx); 82 | 83 | // check for a null row 84 | if (c) { 85 | NSString *s = [NSString stringWithUTF8String:c]; 86 | 87 | [object setValue:s forKey:[NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)]]; 88 | } 89 | } 90 | } 91 | 92 | #pragma clang diagnostic push 93 | #pragma clang diagnostic ignored "-Wdeprecated-implementations" 94 | 95 | - (NSDictionary*)resultDict { 96 | 97 | NSUInteger num_cols = (NSUInteger)sqlite3_data_count([_statement statement]); 98 | 99 | if (num_cols > 0) { 100 | NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:num_cols]; 101 | 102 | NSEnumerator *columnNames = [[self columnNameToIndexMap] keyEnumerator]; 103 | NSString *columnName = nil; 104 | while ((columnName = [columnNames nextObject])) { 105 | id objectValue = [self objectForColumnName:columnName]; 106 | [dict setObject:objectValue forKey:columnName]; 107 | } 108 | 109 | return FMDBReturnAutoreleased([dict copy]); 110 | } 111 | else { 112 | NSLog(@"Warning: There seem to be no columns in this set."); 113 | } 114 | 115 | return nil; 116 | } 117 | 118 | #pragma clang diagnostic pop 119 | 120 | - (NSDictionary*)resultDictionary { 121 | 122 | NSUInteger num_cols = (NSUInteger)sqlite3_data_count([_statement statement]); 123 | 124 | if (num_cols > 0) { 125 | NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:num_cols]; 126 | 127 | int columnCount = sqlite3_column_count([_statement statement]); 128 | 129 | int columnIdx = 0; 130 | for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { 131 | 132 | NSString *columnName = [NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)]; 133 | id objectValue = [self objectForColumnIndex:columnIdx]; 134 | [dict setObject:objectValue forKey:columnName]; 135 | } 136 | 137 | return dict; 138 | } 139 | else { 140 | NSLog(@"Warning: There seem to be no columns in this set."); 141 | } 142 | 143 | return nil; 144 | } 145 | 146 | 147 | 148 | 149 | - (BOOL)next { 150 | return [self nextWithError:nil]; 151 | } 152 | 153 | - (BOOL)nextWithError:(NSError **)outErr { 154 | 155 | int rc = sqlite3_step([_statement statement]); 156 | 157 | if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) { 158 | NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [_parentDB databasePath]); 159 | NSLog(@"Database busy"); 160 | if (outErr) { 161 | *outErr = [_parentDB lastError]; 162 | } 163 | } 164 | else if (SQLITE_DONE == rc || SQLITE_ROW == rc) { 165 | // all is well, let's return. 166 | } 167 | else if (SQLITE_ERROR == rc) { 168 | NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); 169 | if (outErr) { 170 | *outErr = [_parentDB lastError]; 171 | } 172 | } 173 | else if (SQLITE_MISUSE == rc) { 174 | // uh oh. 175 | NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); 176 | if (outErr) { 177 | if (_parentDB) { 178 | *outErr = [_parentDB lastError]; 179 | } 180 | else { 181 | // If 'next' or 'nextWithError' is called after the result set is closed, 182 | // we need to return the appropriate error. 183 | NSDictionary* errorMessage = [NSDictionary dictionaryWithObject:@"parentDB does not exist" forKey:NSLocalizedDescriptionKey]; 184 | *outErr = [NSError errorWithDomain:@"FMDatabase" code:SQLITE_MISUSE userInfo:errorMessage]; 185 | } 186 | 187 | } 188 | } 189 | else { 190 | // wtf? 191 | NSLog(@"Unknown error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); 192 | if (outErr) { 193 | *outErr = [_parentDB lastError]; 194 | } 195 | } 196 | 197 | 198 | if (rc != SQLITE_ROW) { 199 | [self close]; 200 | } 201 | 202 | return (rc == SQLITE_ROW); 203 | } 204 | 205 | - (BOOL)hasAnotherRow { 206 | return sqlite3_errcode([_parentDB sqliteHandle]) == SQLITE_ROW; 207 | } 208 | 209 | - (int)columnIndexForName:(NSString*)columnName { 210 | columnName = [columnName lowercaseString]; 211 | 212 | NSNumber *n = [[self columnNameToIndexMap] objectForKey:columnName]; 213 | 214 | if (n) { 215 | return [n intValue]; 216 | } 217 | 218 | NSLog(@"Warning: I could not find the column named '%@'.", columnName); 219 | 220 | return -1; 221 | } 222 | 223 | 224 | 225 | - (int)intForColumn:(NSString*)columnName { 226 | return [self intForColumnIndex:[self columnIndexForName:columnName]]; 227 | } 228 | 229 | - (int)intForColumnIndex:(int)columnIdx { 230 | return sqlite3_column_int([_statement statement], columnIdx); 231 | } 232 | 233 | - (long)longForColumn:(NSString*)columnName { 234 | return [self longForColumnIndex:[self columnIndexForName:columnName]]; 235 | } 236 | 237 | - (long)longForColumnIndex:(int)columnIdx { 238 | return (long)sqlite3_column_int64([_statement statement], columnIdx); 239 | } 240 | 241 | - (long long int)longLongIntForColumn:(NSString*)columnName { 242 | return [self longLongIntForColumnIndex:[self columnIndexForName:columnName]]; 243 | } 244 | 245 | - (long long int)longLongIntForColumnIndex:(int)columnIdx { 246 | return sqlite3_column_int64([_statement statement], columnIdx); 247 | } 248 | 249 | - (unsigned long long int)unsignedLongLongIntForColumn:(NSString*)columnName { 250 | return [self unsignedLongLongIntForColumnIndex:[self columnIndexForName:columnName]]; 251 | } 252 | 253 | - (unsigned long long int)unsignedLongLongIntForColumnIndex:(int)columnIdx { 254 | return (unsigned long long int)[self longLongIntForColumnIndex:columnIdx]; 255 | } 256 | 257 | - (BOOL)boolForColumn:(NSString*)columnName { 258 | return [self boolForColumnIndex:[self columnIndexForName:columnName]]; 259 | } 260 | 261 | - (BOOL)boolForColumnIndex:(int)columnIdx { 262 | return ([self intForColumnIndex:columnIdx] != 0); 263 | } 264 | 265 | - (double)doubleForColumn:(NSString*)columnName { 266 | return [self doubleForColumnIndex:[self columnIndexForName:columnName]]; 267 | } 268 | 269 | - (double)doubleForColumnIndex:(int)columnIdx { 270 | return sqlite3_column_double([_statement statement], columnIdx); 271 | } 272 | 273 | - (NSString*)stringForColumnIndex:(int)columnIdx { 274 | 275 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 276 | return nil; 277 | } 278 | 279 | const char *c = (const char *)sqlite3_column_text([_statement statement], columnIdx); 280 | 281 | if (!c) { 282 | // null row. 283 | return nil; 284 | } 285 | 286 | return [NSString stringWithUTF8String:c]; 287 | } 288 | 289 | - (NSString*)stringForColumn:(NSString*)columnName { 290 | return [self stringForColumnIndex:[self columnIndexForName:columnName]]; 291 | } 292 | 293 | - (NSDate*)dateForColumn:(NSString*)columnName { 294 | return [self dateForColumnIndex:[self columnIndexForName:columnName]]; 295 | } 296 | 297 | - (NSDate*)dateForColumnIndex:(int)columnIdx { 298 | 299 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 300 | return nil; 301 | } 302 | 303 | return [_parentDB hasDateFormatter] ? [_parentDB dateFromString:[self stringForColumnIndex:columnIdx]] : [NSDate dateWithTimeIntervalSince1970:[self doubleForColumnIndex:columnIdx]]; 304 | } 305 | 306 | 307 | - (NSData*)dataForColumn:(NSString*)columnName { 308 | return [self dataForColumnIndex:[self columnIndexForName:columnName]]; 309 | } 310 | 311 | - (NSData*)dataForColumnIndex:(int)columnIdx { 312 | 313 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 314 | return nil; 315 | } 316 | 317 | const char *dataBuffer = sqlite3_column_blob([_statement statement], columnIdx); 318 | int dataSize = sqlite3_column_bytes([_statement statement], columnIdx); 319 | 320 | if (dataBuffer == NULL) { 321 | return nil; 322 | } 323 | 324 | return [NSData dataWithBytes:(const void *)dataBuffer length:(NSUInteger)dataSize]; 325 | } 326 | 327 | 328 | - (NSData*)dataNoCopyForColumn:(NSString*)columnName { 329 | return [self dataNoCopyForColumnIndex:[self columnIndexForName:columnName]]; 330 | } 331 | 332 | - (NSData*)dataNoCopyForColumnIndex:(int)columnIdx { 333 | 334 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 335 | return nil; 336 | } 337 | 338 | const char *dataBuffer = sqlite3_column_blob([_statement statement], columnIdx); 339 | int dataSize = sqlite3_column_bytes([_statement statement], columnIdx); 340 | 341 | NSData *data = [NSData dataWithBytesNoCopy:(void *)dataBuffer length:(NSUInteger)dataSize freeWhenDone:NO]; 342 | 343 | return data; 344 | } 345 | 346 | 347 | - (BOOL)columnIndexIsNull:(int)columnIdx { 348 | return sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL; 349 | } 350 | 351 | - (BOOL)columnIsNull:(NSString*)columnName { 352 | return [self columnIndexIsNull:[self columnIndexForName:columnName]]; 353 | } 354 | 355 | - (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx { 356 | 357 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 358 | return nil; 359 | } 360 | 361 | return sqlite3_column_text([_statement statement], columnIdx); 362 | } 363 | 364 | - (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName { 365 | return [self UTF8StringForColumnIndex:[self columnIndexForName:columnName]]; 366 | } 367 | 368 | - (id)objectForColumnIndex:(int)columnIdx { 369 | int columnType = sqlite3_column_type([_statement statement], columnIdx); 370 | 371 | id returnValue = nil; 372 | 373 | if (columnType == SQLITE_INTEGER) { 374 | returnValue = [NSNumber numberWithLongLong:[self longLongIntForColumnIndex:columnIdx]]; 375 | } 376 | else if (columnType == SQLITE_FLOAT) { 377 | returnValue = [NSNumber numberWithDouble:[self doubleForColumnIndex:columnIdx]]; 378 | } 379 | else if (columnType == SQLITE_BLOB) { 380 | returnValue = [self dataForColumnIndex:columnIdx]; 381 | } 382 | else { 383 | //default to a string for everything else 384 | returnValue = [self stringForColumnIndex:columnIdx]; 385 | } 386 | 387 | if (returnValue == nil) { 388 | returnValue = [NSNull null]; 389 | } 390 | 391 | return returnValue; 392 | } 393 | 394 | - (id)objectForColumnName:(NSString*)columnName { 395 | return [self objectForColumnIndex:[self columnIndexForName:columnName]]; 396 | } 397 | 398 | // returns autoreleased NSString containing the name of the column in the result set 399 | - (NSString*)columnNameForIndex:(int)columnIdx { 400 | return [NSString stringWithUTF8String: sqlite3_column_name([_statement statement], columnIdx)]; 401 | } 402 | 403 | - (void)setParentDB:(FMDatabase *)newDb { 404 | _parentDB = newDb; 405 | } 406 | 407 | - (id)objectAtIndexedSubscript:(int)columnIdx { 408 | return [self objectForColumnIndex:columnIdx]; 409 | } 410 | 411 | - (id)objectForKeyedSubscript:(NSString *)columnName { 412 | return [self objectForColumnName:columnName]; 413 | } 414 | 415 | 416 | @end 417 | -------------------------------------------------------------------------------- /JKDBModel/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /JKDBModel/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /JKDBModel/Images.xcassets/eay.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "eay@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /JKDBModel/Images.xcassets/eay.imageset/eay@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xhellohaley/JKDBModel/470aaf8b114164774d5409a841eec4d2cfb061d1/JKDBModel/Images.xcassets/eay.imageset/eay@2x.png -------------------------------------------------------------------------------- /JKDBModel/Images.xcassets/portrait.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "portrait.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /JKDBModel/Images.xcassets/portrait.imageset/portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xhellohaley/JKDBModel/470aaf8b114164774d5409a841eec4d2cfb061d1/JKDBModel/Images.xcassets/portrait.imageset/portrait.png -------------------------------------------------------------------------------- /JKDBModel/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.zx.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /JKDBModel/Models/Depart.h: -------------------------------------------------------------------------------- 1 | // 2 | // Depart.h 3 | // JKBaseModel 4 | // 5 | // Created by zx_04 on 15/6/30. 6 | // Copyright (c) 2015年 joker. All rights reserved. 7 | // 8 | 9 | #import "JKDBModel.h" 10 | 11 | @interface Depart : JKDBModel 12 | 13 | /** 部门编号 */ 14 | @property (nonatomic, copy) NSString *departNum; 15 | /** 部门名称 */ 16 | @property (nonatomic, copy) NSString *departName; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /JKDBModel/Models/Depart.m: -------------------------------------------------------------------------------- 1 | // 2 | // Depart.m 3 | // JKBaseModel 4 | // 5 | // Created by zx_04 on 15/6/30. 6 | // Copyright (c) 2015年 joker. All rights reserved. 7 | // 8 | 9 | #import "Depart.h" 10 | 11 | @implementation Depart 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /JKDBModel/Models/User.h: -------------------------------------------------------------------------------- 1 | // 2 | // User.h 3 | // JKBaseModel 4 | // 5 | // Created by zx_04 on 15/6/24. 6 | // Copyright (c) 2015年 joker. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "JKDBModel.h" 11 | 12 | @interface User : JKDBModel 13 | 14 | /** 账号 */ 15 | @property (nonatomic, copy) NSString *account; 16 | /** 名字 */ 17 | @property (nonatomic, copy) NSString *name; 18 | /** 性别 */ 19 | @property (nonatomic, copy) NSString *sex; 20 | /** 头像地址 */ 21 | @property (nonatomic, copy) NSString *portraitPath; 22 | /** 图片 */ 23 | @property (strong, nonatomic) NSData *imageData; 24 | /** 手机号码 */ 25 | @property (nonatomic, copy) NSString *moblie; 26 | /** 简介 */ 27 | @property (nonatomic, copy) NSString *descn; 28 | /** 年龄 */ 29 | @property (nonatomic, assign) int age; 30 | 31 | @property (nonatomic, assign) long long createTime; 32 | 33 | @property (nonatomic, assign) int height; 34 | 35 | @property (nonatomic, assign) int field1; 36 | 37 | @property (nonatomic, assign) int field2; 38 | 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /JKDBModel/Models/User.m: -------------------------------------------------------------------------------- 1 | // 2 | // User.m 3 | // JKBaseModel 4 | // 5 | // Created by zx_04 on 15/6/24. 6 | // Copyright (c) 2015年 joker. All rights reserved. 7 | // 8 | 9 | #import "User.h" 10 | #import "JKDBHelper.h" 11 | 12 | @interface User () 13 | 14 | @property (nonatomic, copy) NSString *duty; 15 | 16 | @end 17 | 18 | @implementation User 19 | 20 | #pragma mark - override method 21 | 22 | +(NSArray *)transients 23 | { 24 | return [NSArray arrayWithObjects:@"field1",@"field2",nil]; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /JKDBModel/README.md: -------------------------------------------------------------------------------- 1 | # JKDBModel 2 | FMDB数据库操作的封装,继承JKDBModel后一行代码实现CURD操作。
3 | 因为项目中主要是多线程中操作数据库,所以Demo里都是多线程操作的方式,该类也可以在主线程中使用。
4 | 依赖FMDB,支持ARC和非ARC。
5 | 不需要实体与数据库映射的属性,添加到transients数组即可。
6 | #怎么使用? 7 | 将项目中DBModel和FMDB两个文件夹拖进工程,然后添加libsqlite3.dylib库。
8 | 创建自己的实体类,继承JKDBModel即可。
9 | 例如User 继承自DBModel,然后可以直接直接调用 User对象的CURD操作。
10 | [user save];
11 | [user saveOrUpdate];
12 | [user deleteObject];
13 | [user update];
14 | [User findAll];
15 | [User findByXXX];
16 | ....... 17 | 18 | #主要方法展示 19 | /**获取该类的所有属性 */ 20 | + (NSDictionary *)getPropertys; 21 | 22 | 23 | /** 获取所有属性,包括主键 */ 24 | + (NSDictionary *)getAllProperties; 25 | 26 | 27 | /** 数据库中是否存在表 */ 28 | + (BOOL)isExistInTable; 29 | 30 | 31 | /** 保存或更新,如果不存在主键保存,有主键,则更新 */ 32 | - (BOOL)saveOrUpdate; 33 | 34 | /** 保存单个数据 */ 35 | - (BOOL)save; 36 | 37 | /** 批量保存数据 */ 38 | + (BOOL)saveObjects:(NSArray *)array; 39 | 40 | /** 更新单个数据 */ 41 | - (BOOL)update; 42 | 43 | /** 批量更新数据*/ 44 | + (BOOL)updateObjects:(NSArray *)array; 45 | 46 | /** 删除单个数据 */ 47 | - (BOOL)deleteObject; 48 | 49 | /** 批量删除数据 */ 50 | + (BOOL)deleteObjects:(NSArray *)array; 51 | 52 | /** 通过条件删除数据 */ 53 | + (BOOL)deleteObjectsByCriteria:(NSString *)criteria; 54 | 55 | 56 | /** 查询全部数据 */ 57 | + (NSArray *)findAll; 58 | 59 | 60 | /** 通过主键查询 */ 61 | + (instancetype)findByPK:(int)inPk; 62 | 63 | /** 查找某条数据 */ 64 | + (instancetype)findFirstByCriteria:(NSString *)criteria; 65 | 66 | 67 | /** 通过条件查找数据,这样可以进行分页查询 @" WHERE pk > 5 limit 10" */ 68 | + (NSArray *)findByCriteria:(NSString *)criteria; 69 | 70 | /** 创建表 */ 71 | + (BOOL)createTable; 72 | + 73 | /** 如果子类中有一些property不需要创建数据库字段,那么这个方法必须在子类中重写 */ 74 | + (NSArray *)transients; 75 | 76 | # 注意事项 77 | 有部分方法是类方法,有些是私有方法。
78 | 有问题的地方欢迎指正。 79 | -------------------------------------------------------------------------------- /JKDBModel/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // JKDBModel 4 | // 5 | // Created by zx_04 on 15/7/1. 6 | // Copyright (c) 2015年 joker. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /JKDBModelTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.Joker.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /JKDBModelTests/JKDBModelTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // JKDBModelTests.m 3 | // JKDBModelTests 4 | // 5 | // Created by Joker on 15/7/1. 6 | // Copyright (c) 2015年 Mac. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface JKDBModelTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation JKDBModelTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | opyright (c) 2009-2015 Matej Bukovinski 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JKDBModel 2 | FMDB的封装,极大简化你的数据库操作,对于自己的扩展也非常简单
3 | 封装类的数据库操作思路与多数FMDB封装不同,是直接实体类对象来做增、删、改、查。
4 | 代码中有比较详细的注释和讲解,有助于理解runtime的机制,如果能帮到你,麻烦给一个`star`。 5 | 对原作者的框架反射部分做了简化,便于理解,原作者的源码github地址在本文尾部。 6 | 7 | # 为什么要封装这个类呢? 8 | 原项目使用的是sqlitepersistentobjects,多线程处理时,问题比较多
9 | 多数FMDB框架功能比较多,代码量也比较大,但是很多功能不常用,而且也不便于理解,因此我考虑用简单易于理解的方式
10 | 来封装一个适用于公司项目的轻量级小框架。
11 | 因为项目需要在多线程下操作数据库,所以demo中的使用案例多数是多线程操作,但是单线程操作也适用。 12 | 13 | # 特点 14 | 改进后的特点如下:
15 | 1.自动创建数据库、自动创建数据库表。
16 | 2.自动检测字段添加新字段。
17 | 3.一行代码实现数据库的CURD操作。
18 | 4.源码及其简单,易于理解和掌握。
19 | 5.扩展自己的功能也非常得简单,容易。
20 | 6.支持多线程,非线程阻塞。
21 | 7.支持arc和mrc。
22 | 23 | # how 怎么使用JKDBModel 24 | 使用JKDBModel非常的简单,只需要将FMDB和DBModel拖入项目中,然后添加`libsqlite3.dylib`
25 | 然后让你的实体类继承自JKDBModel,你的实体类就具备了操作数据库的功能。 26 | 27 | # demo中有CURD演示操作 28 | 效果图
29 | ![](http://cc.cocimg.com/bbs/attachment/postcate/topic/16/313017_189_ccde14372754000f44c3edbcc68c9.png "CURD示例") 30 | 31 | ## CURD操作 32 | 操作都有保存和批量保存两种方式。
33 | 例如
34 | 保存操作:`[user save]`
35 | 批量保存:`[User saveObjects:array]` 36 | 37 | # 数据库操作api 38 | ```Objective-C 39 | /** 数据库中是否存在表 */ 40 | + (BOOL)isExistInTable; 41 | /** 保存或更新 42 | * 如果不存在主键,保存, 43 | * 有主键,则更新 44 | */ 45 | - (BOOL)saveOrUpdate; 46 | /** 保存单个数据 */ 47 | - (BOOL)save; 48 | /** 批量保存数据 */ 49 | + (BOOL)saveObjects:(NSArray *)array; 50 | /** 更新单个数据 */ 51 | - (BOOL)update; 52 | /** 批量更新数据*/ 53 | + (BOOL)updateObjects:(NSArray *)array; 54 | /** 删除单个数据 */ 55 | - (BOOL)deleteObject; 56 | /** 批量删除数据 */ 57 | + (BOOL)deleteObjects:(NSArray *)array; 58 | /** 通过条件删除数据 */ 59 | + (BOOL)deleteObjectsByCriteria:(NSString *)criteria; 60 | /** 清空表 */ 61 | + (BOOL)clearTable; 62 | 63 | /** 查询全部数据 */ 64 | + (NSArray *)findAll; 65 | 66 | /** 通过主键查询 */ 67 | + (instancetype)findByPK:(int)inPk; 68 | 69 | /** 查找某条数据 */ 70 | + (instancetype)findFirstByCriteria:(NSString *)criteria; 71 | 72 | /** 通过条件查找数据 73 | * 这样可以进行分页查询 @" WHERE pk > 5 limit 10" 74 | */ 75 | + (NSArray *)findByCriteria:(NSString *)criteria; 76 | /** 77 | * 创建表 78 | * 如果已经创建,返回YES 79 | */ 80 | + (BOOL)createTable; 81 | 82 | #pragma mark - must be override method 83 | /** 如果子类中有一些property不需要创建数据库字段,那么这个方法必须在子类中重写 84 | */ 85 | + (NSArray *)transients; 86 | ``` 87 | # 更新 88 | 修改了创建表和字段检测功能方法。
89 | 由于实际项目中,一个账号对应一个文件夹放数据库,经常会有切换账号登录的需求,在initialize创建数据库表,只执行一次,数据库不能保证一定创建,因此添加切换数据库功能,动态创建功能。 2015-08-25
90 | 添加条件查询和删除的新方法:
91 | ``` 92 | + (instancetype)findFirstWithFormat:(NSString *)format, ...; 93 | 94 | + (NSArray *)findWithFormat:(NSString *)format, ...; 95 | 96 | /** 通过条件删除 (多参数)--2 */ 97 | + (BOOL)deleteObjectsWithFormat:(NSString *)format, ...; 98 | ``` 99 | 更新于2015-09-08 100 | 101 | # 提醒 102 | SQLite 默认支持五种数据类型TEXT、INTEGER、REAL、BLOB、NULL,我只做了少数类型的判断,
103 | 如:int、unsigned int、short、unsigned short、BOOL,对象类型默认是字符串。
104 | 当然你可以详细的把所有的类型判断都做出来,代码中有部分常用的表示符号,详细的可以查看apple 文档。
105 | 理解代码比会使用代码更重要,望使用的时候先理解一下实现思路。
106 | 107 | 108 | # 鸣谢 109 | 动态获取Model的属性部分源自:https://github.com/li6185377/LKDaoBase
110 | 增删改查封装思想源自:`SQLitePersistentObject`,与之前做Java时的数据库操作类似,更简易。 111 | 112 | 如果你有什么问题或者有更好的建议,请告知我,我会及时更正,修改。 113 | --------------------------------------------------------------------------------