├── .gitignore ├── README.md ├── main.qml ├── sqml.qmlproject ├── sqml ├── CrudDao.qml ├── CrudService.qml ├── DatabaseConnection.qml ├── SqlMapping.qml ├── SqlQueryBuilder.qml └── qmldir ├── tests ├── test_sql_builder.qml └── tests.qmlproject └── user ├── DataBase.qml ├── UserDao.qml ├── UserDaoSqlMapping.qml └── UserService.qml /.gitignore: -------------------------------------------------------------------------------- 1 | *.user 2 | *.jsc 3 | *.qmlc 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sqml 2 | 3 | Simple sqlite sql builder (very simple sql orm) for qml 4 | 5 | ## Layering CRUD 6 | 7 | ## Sql query builder 8 | 9 | ``` 10 | builder.select(['name', 'sum(a.id)']) 11 | .from('user a') 12 | .where() 13 | .gt('a.age', 10) 14 | .orderBy(['name', 'age']); 15 | console.log('sql: ', builder.dumpSql()); 16 | console.log('bind: ', builder.dumpBind()); 17 | ``` 18 | 19 | ``` 20 | sql: SELECT num, sum(a.id) FROM user a 21 | WHERE a.age > 10 22 | ORDER BY name, age 23 | 24 | bind: [10] 25 | ``` 26 | -------------------------------------------------------------------------------- /main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import "user" 4 | 5 | Item { 6 | 7 | width: 360 8 | height: 360 9 | 10 | DataBase { 11 | id: dataBase 12 | } 13 | 14 | UserService { 15 | id: userService 16 | connection: dataBase 17 | debug: false 18 | } 19 | 20 | Component.onCompleted: { 21 | dataBase.transaction(function (tx){ 22 | tx.executeSql('CREATE TABLE IF NOT EXISTS User(id TEXT, name TEXT)'); 23 | }); 24 | } 25 | 26 | Component.onDestruction: { 27 | dataBase.transaction(function (tx){ 28 | tx.executeSql('DROP TABLE User'); 29 | }); 30 | } 31 | 32 | MouseArea { 33 | anchors.fill: parent 34 | onClicked: { 35 | var sqlId = Date.now(); 36 | var user = { 37 | id: sqlId, 38 | name: Math.random().toString().substring(0, 12) 39 | } 40 | 41 | userService.insert(user, function(size){ 42 | console.log("size: " , size); 43 | }); 44 | 45 | console.log("sqlId", sqlId) 46 | 47 | userService.findList({id: sqlId}, function(list){ 48 | for(var iter in list) { 49 | console.log("user: ", JSON.stringify(list[iter])); 50 | } 51 | }); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /sqml.qmlproject: -------------------------------------------------------------------------------- 1 | /* File generated by Qt Creator */ 2 | 3 | import QmlProject 1.1 4 | 5 | Project { 6 | mainFile: "main.qml" 7 | 8 | /* Include .qml, .js, and image files from current directory and subdirectories */ 9 | QmlFiles { 10 | directory: "." 11 | } 12 | JavaScriptFiles { 13 | directory: "." 14 | } 15 | ImageFiles { 16 | directory: "." 17 | } 18 | Files { 19 | directory: "." 20 | filter: "*.md" 21 | } 22 | Files { 23 | directory: "." 24 | filter: "qmldir" 25 | } 26 | 27 | /* List of plugin directories passed to QML runtime */ 28 | importPaths: [ "." ] 29 | } 30 | -------------------------------------------------------------------------------- /sqml/CrudDao.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | // CrudDao 4 | QtObject { 5 | id: dao 6 | 7 | readonly property var get: dao.__getImpl 8 | readonly property var getByEntity: dao.__getByEntityImpl 9 | readonly property var findList: dao.__findListImpl 10 | readonly property var insert: dao.__insertImpl 11 | readonly property var insertList: dao.__insertListImpl 12 | readonly property var update: dao.__updateImpl 13 | readonly property var deleteById: dao.__deleteByIdImpl 14 | readonly property var deleteRecord: dao.__deleteRecordImpl 15 | 16 | property bool debug: __connection != null ? __connection.debug : false 17 | 18 | //protected: 19 | property DatabaseConnection __connection: null 20 | property SqlMapping __sqlMapping: null 21 | 22 | readonly property int maxInsertCount: 500 23 | 24 | //@abstract 25 | function __getImpl(entity, callback, error) { 26 | __executeSqlImpl(entity, __sqlMapping.get, false, function(results){ 27 | if(results.rows.length > 1) { 28 | throw "getById should be only one result, result size: " 29 | + results.rows.length; 30 | } 31 | callback(results.rows.item(0)); 32 | }, error); 33 | } 34 | 35 | //@abstract 36 | function __getByEntityImpl(entity, callback, error) { 37 | __executeSqlImpl(entity, __sqlMapping.getByEntity, true, function(results){ 38 | if(results.rows.length > 1) { 39 | throw "getById should be only one result, result size: " 40 | + results.rows.length; 41 | } 42 | callback(results.rows.item(0)); 43 | }, error); 44 | } 45 | 46 | //@abstract 47 | function __findListImpl(entity, callback, error) { 48 | __executeSqlImpl(entity, __sqlMapping.findList, true, function(results){ 49 | var resultList = []; 50 | for(var i = 0; i < results.rows.length; i++) { 51 | resultList.push(results.rows.item(i)); 52 | } 53 | callback(resultList); 54 | }, error); 55 | } 56 | 57 | //@abstract 58 | function __insertImpl(entity, callback, error) { 59 | __executeSqlImpl(entity, __sqlMapping.insert, false, function(results){ 60 | callback(results.rowsAffected); 61 | }, error); 62 | } 63 | 64 | //@abstract 65 | function __insertListImpl(list, callback, error) { 66 | if (typeof list === 'undefined' || list === null || list.length === 0) { 67 | error('__insertListImpl fail : list is empty'); 68 | } 69 | 70 | // Avoid Error: too many SQL variables 71 | if (list.length > maxInsertCount) { 72 | error('__insertListImpl fail : list count:' 73 | + list.length +' more than maxInsertCount'); 74 | } 75 | 76 | __executeSqlImpl(list, __sqlMapping.insertList, false, function(results){ 77 | callback(results.rowsAffected); 78 | }, error); 79 | } 80 | 81 | //@abstract 82 | function __updateImpl(entity, callback, error) { 83 | __executeSqlImpl(entity, __sqlMapping.update, false, function(results){ 84 | callback(results.rowsAffected); 85 | }, error); 86 | } 87 | 88 | //@abstract 89 | function __deleteByIdImpl(entity, callback, error) { 90 | __executeSqlImpl(entity, __sqlMapping.deleteById, false, function(results){ 91 | callback(results.rowsAffected); 92 | }, error); 93 | } 94 | 95 | //@abstract 96 | function __deleteRecordImpl(entity, callback, error) { 97 | __executeSqlImpl(entity, __sqlMapping.deleteRecord, false, function(results){ 98 | callback(results.rowsAffected); 99 | }, error); 100 | } 101 | 102 | function __checkImpl(entity) { 103 | return connection != null 104 | && __sqlMapping != null 105 | && typeof entity !== 'undefined'; 106 | } 107 | 108 | 109 | function __executeSqlImpl(entity, mapping, readOnly, callback, error) { 110 | if(!__checkImpl(entity)) { 111 | console.error(JSON.stringify(entity), __connection, __sqlMapping); 112 | error(new Error("check error")); 113 | return; 114 | } 115 | 116 | var ret = mapping(entity); 117 | //! [0] avoid have a blank char before SELECT 118 | var sql = ret.sql.trim(); 119 | //! [0] 120 | var bind = ret.bind; 121 | 122 | if(debug) { 123 | console.debug("__executeSqlImpl : ", sql, " bind:", bind); 124 | } 125 | 126 | if(typeof sql === '' || sql === "") { 127 | error(new Error("sql is empty")); 128 | return; 129 | } 130 | 131 | var transaction = readOnly ? connection.readTransaction 132 | : connection.transaction; 133 | transaction(function(tx){ 134 | try { 135 | var resultList = tx.executeSql(sql, bind) 136 | if(debug) { 137 | console.debug("resultList lenght : ", resultList.rows.length 138 | , " rowsAffected : ", resultList.rowsAffected 139 | , " insertId : ", resultList.insertId); 140 | } 141 | callback(resultList); 142 | } catch(e) { 143 | console.error("sql: ", sql); 144 | error(e); 145 | } 146 | }); 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /sqml/CrudService.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | QtObject { 4 | id: service 5 | 6 | property DatabaseConnection connection 7 | readonly property alias dao: service.__dao 8 | property bool debug: connection != null ? connection.debug : false 9 | 10 | //protected: 11 | property CrudDao __dao 12 | 13 | function get(entity, callback, error) { 14 | error = error || __errorImpl; 15 | dao.get(entity, callback, error); 16 | } 17 | 18 | function getByEntity(entity, callback, error) { 19 | error = error || __errorImpl; 20 | dao.getByEntity(entity, callback, error); 21 | } 22 | 23 | function findList(entity, callback, error) { 24 | error = error || __errorImpl; 25 | dao.findList(entity, callback, error); 26 | } 27 | 28 | function insert(entity, callback, error) { 29 | error = error || __errorImpl; 30 | dao.insert(entity, callback, error); 31 | } 32 | 33 | function insertList(list, callback, error) { 34 | error = error || __errorImpl; 35 | dao.insertList(list, callback, error); 36 | } 37 | 38 | function update(entity, callback, error) { 39 | error = error || __errorImpl; 40 | dao.update(entity, callback, error); 41 | } 42 | 43 | function deleteById(entity, callback, error) { 44 | error = error || __errorImpl; 45 | dao.deleteById(entity, callback, error); 46 | } 47 | 48 | function deleteRecord(entity, callback, error) { 49 | error = error || __errorImpl; 50 | dao.deleteById(entity, callback, error); 51 | } 52 | 53 | function __errorImpl(error) { 54 | console.trace(); 55 | console.log(error); 56 | if(error instanceof Error) { 57 | throw error; 58 | } else { 59 | throw new Error(error); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /sqml/DatabaseConnection.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.LocalStorage 2.0 3 | 4 | // DatabaseConnection 5 | QtObject { 6 | id: databaseConnection 7 | 8 | property string identifier: "" 9 | property string version: "" 10 | property string description: "" 11 | property int estimatedSize: 0 12 | readonly property alias isOpen: databaseConnection.__isOpen 13 | property bool debug: false 14 | 15 | readonly property var database: databaseConnection.__database 16 | 17 | property var __database 18 | property bool __isOpen: false 19 | 20 | // onIdentifierChanged: { 21 | // __reOpen(); 22 | // } 23 | 24 | //@abstract 25 | function initDatabase(db) { 26 | } 27 | 28 | function __reOpen() { 29 | __isOpen = false; 30 | __tryOpen(); 31 | } 32 | 33 | function __tryOpen() { 34 | if(!__isOpen) { 35 | __openDatabase(); 36 | } 37 | } 38 | 39 | function __openDatabase() { 40 | if(__isOpen) { 41 | return; 42 | } 43 | 44 | __isOpen = true; 45 | 46 | console.debug("identifier", identifier, 47 | "version:", version, 48 | "description:", description, 49 | "estimatedSize:", estimatedSize); 50 | 51 | if(identifier !== '' && version !== '' && description !== '' && estimatedSize != 0) { 52 | try { 53 | __database = LocalStorage.openDatabaseSync(identifier, 54 | version, 55 | description, 56 | estimatedSize, 57 | initDatabase); 58 | 59 | if (__database.version === '') { 60 | // FIX: Error: SQL: database version mismatch 61 | __database.changeVersion('', version); 62 | } 63 | 64 | } catch(e) { 65 | __isOpen = false; 66 | console.trace() 67 | 68 | throw e; 69 | } 70 | } else { 71 | __isOpen = false; 72 | console.debug("identifier", identifier, 73 | "version:", version, 74 | "description:", description, 75 | "estimatedSize:", estimatedSize); 76 | throw "arguments lost!"; 77 | } 78 | } 79 | 80 | function changeVersion(from, to, callback) { 81 | __tryOpen(); 82 | 83 | if(typeof from === 'undefined' || from === '') { 84 | return; 85 | } 86 | 87 | if(typeof to === 'undefined' || to === '') { 88 | return; 89 | } 90 | 91 | if(from === to) { 92 | return; 93 | } 94 | 95 | database.changeVersion(from, to, callback); 96 | version = to; 97 | } 98 | 99 | function transaction(callback) { 100 | __tryOpen(); 101 | 102 | database.transaction(callback); 103 | } 104 | 105 | function readTransaction(callback) { 106 | __tryOpen(); 107 | 108 | database.readTransaction(callback); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /sqml/SqlMapping.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | // SqlMapping 4 | QtObject { 5 | id: sqlMapping 6 | 7 | property bool debug: false 8 | 9 | readonly property alias sqlQueryBuilder: sqlMapping.__sqlQueryBuilder 10 | 11 | readonly property SqlQueryBuilder __sqlQueryBuilder: SqlQueryBuilder { 12 | } 13 | 14 | //@abstract 15 | function get(entity) { 16 | return sqlQueryBuilder.dump(); 17 | } 18 | 19 | //@abstract 20 | function getByEntity(entity) { 21 | return sqlQueryBuilder.dump(); 22 | } 23 | 24 | //@abstract 25 | function findList(entity) { 26 | return sqlQueryBuilder.dump(); 27 | } 28 | 29 | //@abstract 30 | function insert(entity) { 31 | return sqlQueryBuilder.dump(); 32 | } 33 | 34 | //@abstract 35 | function insertList(list) { 36 | return sqlQueryBuilder.dump(); 37 | } 38 | 39 | //@abstract 40 | function update(entity) { 41 | return sqlQueryBuilder.dump(); 42 | } 43 | 44 | //@abstract 45 | function deleteById(entity) { 46 | return sqlQueryBuilder.dump(); 47 | } 48 | 49 | //@abstract 50 | function deleteRecord(entity) { 51 | return sqlQueryBuilder.dump(); 52 | } 53 | 54 | function stringNotEmpty(str) { 55 | return typeof str !== 'undefined' && str !== ''; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /sqml/SqlQueryBuilder.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | QtObject { 4 | id: builder 5 | 6 | readonly property string spaceString: ' '; 7 | 8 | property alias sqlQuery: builder.__sqlQuery 9 | property alias bind: builder.__bind 10 | 11 | property string __sqlQuery: '' 12 | property var __bind: [] 13 | 14 | function dumpSql() { 15 | var dumpStr = sqlQuery.trim(); 16 | __sqlQuery = ''; 17 | return dumpStr; 18 | } 19 | 20 | function dumpBind() { 21 | var bind = __bind; 22 | __bind = []; 23 | return bind; 24 | } 25 | 26 | function dump() { 27 | var query = { 28 | sql: dumpSql(), 29 | bind: dumpBind() 30 | } 31 | return query; 32 | } 33 | 34 | function select(fields) { 35 | var fieldsType = typeof fields; 36 | var selectPrefix = 'SELECT' + spaceString; 37 | 38 | switch(fieldsType) { 39 | case 'string': 40 | __sqlQuery += selectPrefix + fields + spaceString; 41 | break; 42 | 43 | case 'object': 44 | case 'array': 45 | var _sql = __fields(fields); 46 | __sqlQuery += selectPrefix + _sql + spaceString; 47 | break; 48 | 49 | case 'undefined': 50 | default: 51 | throw new TypeError('fields is not a string or array or object type: ' 52 | + fieldsType); 53 | } 54 | 55 | return builder; 56 | } 57 | 58 | function from(tableName) { 59 | __checkTableName(tableName); 60 | 61 | var fromPrefix = spaceString + 'FROM' + spaceString; 62 | 63 | 64 | __sqlQuery += fromPrefix + tableName + spaceString; 65 | 66 | return builder; 67 | } 68 | 69 | function where(callback) { 70 | var argLen = arguments.length; 71 | 72 | if (argLen == 0) { 73 | var wherePrefix = spaceString + 'WHERE' + spaceString; 74 | 75 | __sqlQuery += wherePrefix; 76 | } else if (argLen == 1) { 77 | __where2(callback); 78 | } 79 | 80 | return builder; 81 | } 82 | 83 | function __where2(callback) { 84 | callback = callback || function(_builder) { 85 | }; 86 | var wherePrefix = spaceString + 'WHERE' + spaceString; 87 | var component = Qt.createComponent("./SqlQueryBuilder.qml"); 88 | 89 | var __builder = component.createObject(builder); 90 | 91 | callback(__builder); 92 | 93 | var __dump = __builder.dump(); 94 | 95 | try { 96 | __builder.destroy(); 97 | } catch(e) { 98 | console.error(e); 99 | } 100 | 101 | if (__dump.sql === '') { 102 | return builder; 103 | } 104 | 105 | __sqlQuery += wherePrefix; 106 | __sqlQuery += __dump.sql; 107 | __bind = __bind.concat(__dump.bind); 108 | } 109 | 110 | 111 | function and() { 112 | var andPrefix = spaceString + 'AND' + spaceString; 113 | __sqlQuery += andPrefix; 114 | return builder; 115 | } 116 | 117 | function or() { 118 | var orPrefix = spaceString + 'OR' + spaceString; 119 | 120 | __sqlQuery += orPrefix; 121 | 122 | return builder; 123 | } 124 | 125 | function lt(field, value) { 126 | var result = __binaryOperator(field, '<', value); 127 | __sqlQuery += result.sql; 128 | __bind = __bind.concat(result.bind); 129 | return builder; 130 | } 131 | 132 | function gt(field, value) { 133 | var result = __binaryOperator(field, '>', value); 134 | __sqlQuery += result.sql; 135 | __bind = __bind.concat(result.bind); 136 | return builder; 137 | } 138 | 139 | function lte(field, value) { 140 | var result = __binaryOperator(field, '<=', value); 141 | __sqlQuery += result.sql; 142 | __bind = __bind.concat(result.bind); 143 | return builder; 144 | } 145 | 146 | function gte(field, value) { 147 | var result = __binaryOperator(field, '>=', value); 148 | __sqlQuery += result.sql; 149 | __bind = __bind.concat(result.bind); 150 | return builder; 151 | } 152 | 153 | function equals(field, value) { 154 | var result = __binaryOperator(field, '=', value); 155 | __sqlQuery += result.sql; 156 | __bind = __bind.concat(result.bind); 157 | return builder; 158 | } 159 | 160 | function notEquals(field, value) { 161 | var result = __binaryOperator(field, '!=', value); 162 | __sqlQuery += result.sql; 163 | __bind = __bind.concat(result.bind); 164 | return builder; 165 | } 166 | 167 | function like(field, value) { 168 | var result = __binaryOperator(field, 'like', value); 169 | __sqlQuery += result.sql; 170 | __bind = __bind.concat(result.bind); 171 | return builder; 172 | } 173 | 174 | function inValues(field, values) { 175 | var inPrefix = spaceString + "IN" + spaceString; 176 | var fieldType = typeof field; 177 | var valuesType = typeof values; 178 | 179 | if(fieldType !== 'string') { 180 | throw new TypeError('field ' + field + ' is not a string type: ' 181 | + fieldType ); 182 | } 183 | 184 | if(valuesType !== 'object') { 185 | throw new TypeError('values ' + values + ' is not a obejct type: ' 186 | + valuesType ); 187 | } 188 | 189 | var result = __values(values); 190 | 191 | __bind = __bind.concat(result.bind); 192 | __sqlQuery += spaceString + field + inPrefix + result.sql; 193 | 194 | return builder; 195 | } 196 | 197 | function orderBy(fields) { 198 | var fieldsType = typeof fields; 199 | var orderByPrefix = spaceString + "ORDER BY" + spaceString; 200 | 201 | if(fieldsType != 'object') { 202 | throw new TypeError('fields ' + fields + ' is not a obejct type: ' 203 | + fieldsType ); 204 | } 205 | 206 | var fieldStr = __fields(fields); 207 | 208 | __sqlQuery += orderByPrefix + fieldStr; 209 | return builder; 210 | } 211 | 212 | function groupBy(fields) { 213 | var fieldsType = typeof fields; 214 | var groupByPrefix = spaceString + "GROUP BY" + spaceString; 215 | 216 | if(fieldsType != 'object') { 217 | throw new TypeError('fields ' + fields + ' is not a obejct type: ' 218 | + fieldsType ); 219 | } 220 | 221 | var fieldStr = __fields(fields); 222 | 223 | __sqlQuery += groupByPrefix + fieldStr; 224 | return builder; 225 | } 226 | 227 | function __binaryOperator(field, op, value) { 228 | var opType = typeof op; 229 | var fieldType = typeof field; 230 | var valueType = typeof value; 231 | 232 | if(fieldType !== 'string') { 233 | throw new TypeError('field ' + field + ' is not a string type: ' 234 | + fieldType ); 235 | } 236 | 237 | if(opType !== 'string') { 238 | throw new TypeError('op ' + op + ' is not a string type: ' 239 | + opType); 240 | } 241 | 242 | if(valueType === 'undefined') { 243 | throw new TypeError('value is undefined type!'); 244 | } 245 | 246 | var opStr = spaceString + op + spaceString; 247 | var sql = spaceString + field + opStr + "?"; 248 | var bind_ = []; 249 | 250 | bind_.push(value); 251 | 252 | return { 253 | sql: sql, 254 | bind: bind_ 255 | } 256 | } 257 | 258 | function insertInto(tableName, fields, values) { 259 | var insertPrefix = 'INSERT INTO' + spaceString; 260 | var valuesPrefix = spaceString + "VALUES" + spaceString; 261 | 262 | var argLength = arguments.length; // arguemnts length 263 | 264 | var fieldsType = typeof fields; 265 | var valuesType = typeof values; 266 | 267 | __checkTableName(tableName); 268 | 269 | var sqlStr = ''; 270 | var valuesResult; 271 | 272 | if(argLength == 2) { 273 | // insert into table values(); 274 | sqlStr = insertPrefix + tableName + spaceString + valuesPrefix; 275 | 276 | valuesResult = __values(fields); 277 | 278 | __sqlQuery += sqlStr + valuesResult.sql; 279 | __bind = __bind.concat(valuesResult.bind); 280 | 281 | 282 | } else if(argLength == 3) { 283 | var fieldsStr = __fields(fields); 284 | 285 | sqlStr = insertPrefix + tableName + 286 | "("+fieldsStr +")"; 287 | 288 | valuesResult = __values(values); 289 | 290 | __sqlQuery += sqlStr + valuesPrefix +valuesResult.sql; 291 | __bind = __bind.concat(valuesResult.bind); 292 | 293 | } else { 294 | throw new TypeError('insert need 2 or 3 argument!'); 295 | } 296 | } 297 | 298 | 299 | function insertMutilValues(tableName, fields, mutilValues) { 300 | var insertPrefix = 'INSERT INTO' + spaceString; 301 | var valuesPrefix = spaceString + "VALUES" + spaceString; 302 | 303 | var argLength = arguments.length; // arguemnts length 304 | 305 | var fieldsType = typeof fields; 306 | var mutilValuesType = typeof mutilValues; 307 | 308 | __checkTableName(tableName); 309 | 310 | var fieldsStr = __fields(fields); 311 | 312 | var sqlStrInsertInto = insertPrefix + tableName + 313 | "("+fieldsStr +")"; 314 | 315 | var bind = []; 316 | var sqlQuery = ''; 317 | 318 | var sqlStr = ''; 319 | 320 | for(var iter in mutilValues) { 321 | 322 | var valuesResult = __values(mutilValues[iter]); 323 | 324 | sqlStr += valuesResult.sql + ", "; 325 | 326 | bind = bind.concat(valuesResult.bind); 327 | } 328 | 329 | sqlStr = sqlStr.substring(0, sqlStr.length - 2); 330 | 331 | __sqlQuery += sqlStrInsertInto + valuesPrefix + sqlStr 332 | __bind = __bind.concat(bind); 333 | } 334 | 335 | 336 | 337 | function update(tableName, map) { 338 | __checkTableName(tableName); 339 | 340 | var updatePrefix = "UPDATE" + spaceString 341 | + tableName + spaceString 342 | + "SET" + spaceString; 343 | 344 | var sqlStr = ''; 345 | var bind_ = []; 346 | for(var iter in map) { 347 | var result = __binaryOperator(iter, '>', map[iter]); 348 | sqlStr += result.sql + ","; 349 | bind_ = bind_.concat(result.bind); 350 | } 351 | 352 | sqlStr = sqlStr.substring(0, sqlStr.length-1).trim(); 353 | 354 | __sqlQuery += updatePrefix + sqlStr; 355 | __bind = __bind.concat(bind_); 356 | 357 | return builder; 358 | } 359 | 360 | function deleteFrom(tableName) { 361 | __checkTableName(tableName); 362 | 363 | var deleteFromPrefix = "DELETE FROM " + tableName + spaceString; 364 | __sqlQuery += deleteFromPrefix; 365 | 366 | return builder; 367 | } 368 | 369 | function __fields(fields) { 370 | var _sql = ''; 371 | for(var fieldIter in fields) { 372 | _sql += fields[fieldIter] + ", " ; 373 | } 374 | if(_sql.length != 0) { 375 | _sql = _sql.substring(0, _sql.length - 2); 376 | } 377 | return _sql; 378 | } 379 | 380 | function __values(values) { 381 | var bind_ = []; 382 | var valuesBind = '('; 383 | for(var valueIter in values) { 384 | bind_.push(values[valueIter]); 385 | valuesBind += " ?," 386 | } 387 | valuesBind = valuesBind.substring(0, valuesBind.length - 1).trim() + ')'; 388 | 389 | return { 390 | sql: valuesBind, 391 | bind: bind_ 392 | } 393 | } 394 | 395 | function __checkTableName(tableName) { 396 | var tableNameType = typeof tableName; 397 | if(tableNameType !== 'string') { 398 | throw new TypeError('tableName is string type: ' 399 | + tableNameType); 400 | } 401 | } 402 | } 403 | -------------------------------------------------------------------------------- /sqml/qmldir: -------------------------------------------------------------------------------- 1 | module sqml 2 | 3 | # Simple layering CRUD for qml 4 | # The module identifier directive must be the first line of the file. Exactly one module identifier directive may exist in the qmldir file. 5 | 6 | CrudDao 1.0 CrudDao.qml 7 | CrudService 1.0 CrudService.qml 8 | DatabaseConnection 1.0 DatabaseConnection.qml 9 | SqlMapping 1.0 SqlMapping.qml 10 | SqlQueryBuilder 1.0 SqlQueryBuilder.qml 11 | -------------------------------------------------------------------------------- /tests/test_sql_builder.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import "../sqml" 4 | 5 | Item { 6 | 7 | width: 320 8 | height: 240 9 | 10 | SqlQueryBuilder { 11 | id: builder 12 | } 13 | 14 | function test_select() { 15 | builder 16 | .select(['name', 'sum(a.id)']) 17 | .from('a.user') 18 | .where() 19 | .gt('a.age', 10) 20 | .or() 21 | .inValues('a.name', ['name', 'mike']) 22 | .orderBy(['name', 'age']) 23 | console.log(builder.dumpSql()); 24 | console.log(builder.dumpBind()); 25 | } 26 | 27 | function test_insert() { 28 | builder 29 | .insertInto('user', ['name', 'age'], ['mike', 10]) 30 | console.log(builder.dumpSql()); 31 | console.log(builder.dumpBind()); 32 | 33 | builder 34 | .insertInto('user', ['make', 10]) 35 | console.log(builder.dumpSql()); 36 | console.log(builder.dumpBind()); 37 | } 38 | 39 | function test_update() { 40 | builder.update('user', { 41 | 'name': 'mike', 42 | "age": 10 43 | }) 44 | .where() 45 | .equals('age', 11); 46 | console.log(builder.dumpSql()); 47 | console.log(builder.dumpBind()); 48 | } 49 | 50 | function test_delete() { 51 | builder.deleteFrom('user') 52 | .where() 53 | .like('name', '%mike%'); 54 | console.log(builder.dumpSql()); 55 | console.log(builder.dumpBind()); 56 | } 57 | 58 | MouseArea { 59 | anchors.fill: parent 60 | onClicked: { 61 | 62 | test_delete(); 63 | 64 | } 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /tests/tests.qmlproject: -------------------------------------------------------------------------------- 1 | /* File generated by Qt Creator */ 2 | 3 | import QmlProject 1.1 4 | 5 | Project { 6 | mainFile: "test_sql_builder.qml" 7 | 8 | /* Include .qml, .js, and image files from current directory and subdirectories */ 9 | QmlFiles { 10 | directory: "." 11 | } 12 | JavaScriptFiles { 13 | directory: "." 14 | } 15 | ImageFiles { 16 | directory: "." 17 | } 18 | Files { 19 | directory: "." 20 | filter: "*.md" 21 | } 22 | 23 | /* List of plugin directories passed to QML runtime */ 24 | // importPaths: [ "../exampleplugin" ] 25 | } 26 | -------------------------------------------------------------------------------- /user/DataBase.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import sqml 1.0 4 | 5 | DatabaseConnection { 6 | id: database 7 | 8 | identifier: "SQML" 9 | version: "1.0" 10 | description: "SQML is a curd layer lib" 11 | estimatedSize: 1000000 12 | } 13 | -------------------------------------------------------------------------------- /user/UserDao.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import sqml 1.0 4 | 5 | // UserDao 6 | CrudDao { 7 | id: userDao 8 | __sqlMapping: UserDaoSqlMapping { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /user/UserDaoSqlMapping.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import sqml 1.0 4 | 5 | SqlMapping { 6 | id: userDaoMapping 7 | 8 | //@override 9 | function get(entity) { 10 | 11 | sqlQueryBuilder.select(['id', 'name']) 12 | .from('user'); 13 | 14 | if(stringNotEmpty(entity.id)) { 15 | sqlQueryBuilder.where(); 16 | sqlQueryBuilder.equals('id', entity.id); 17 | } 18 | 19 | return sqlQueryBuilder.dump(); 20 | } 21 | 22 | //@override 23 | function getByEntity(entity) { 24 | return get(entity); 25 | } 26 | 27 | //@override 28 | function findList(entity) { 29 | 30 | sqlQueryBuilder.select(['id', 'name']) 31 | .from('user'); 32 | 33 | if(stringNotEmpty(entity.id)) { 34 | sqlQueryBuilder.where(); 35 | sqlQueryBuilder.equals('id', entity.id); 36 | } 37 | 38 | return sqlQueryBuilder.dump(); 39 | } 40 | 41 | //@override 42 | function insert(entity) { 43 | sqlQueryBuilder.insertInto('user', 44 | ['id', 'name'], 45 | [entity.id, entity.name]); 46 | 47 | return sqlQueryBuilder.dump(); 48 | } 49 | 50 | //@override 51 | function update(entity) { 52 | sqlQueryBuilder.update('user', { 53 | 'name': entity.name 54 | }) 55 | .where() 56 | .equals('id', entity.id); 57 | 58 | return sqlQueryBuilder.dump(); 59 | } 60 | 61 | //@override 62 | function deleteById(entity) { 63 | sqlQueryBuilder.deleteFrom('user') 64 | .where() 65 | .equals('id', entity.id); 66 | 67 | return sqlQueryBuilder.dump(); 68 | } 69 | 70 | //@override 71 | function deleteRecord(entity) { 72 | return deleteById(entity) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /user/UserService.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import sqml 1.0 4 | 5 | CrudService { 6 | id: userService 7 | 8 | __dao: UserDao { 9 | __connection: userService.connection 10 | debug: userService.debug 11 | } 12 | } 13 | --------------------------------------------------------------------------------