UNPKG

mybatis-node

Version:

MyBatisNodeJs is a port from the The MyBatis Java data mapper framework for Node.Js.

502 lines (487 loc) 18 kB
import * as fs from "fs"; import * as moment from "moment"; import * as path from "path"; import * as s from "string"; import * as util from "util"; import {DOMParser} from "xmldom"; import Context from "./Context"; class SqlCommand { public sql; public parameters; constructor() { this.sql = ""; this.parameters = []; } public addParameter(value) { this.parameters.push(value); } } class No { public id; public mapping; public children; constructor(id, mapping) { this.id = id; this.children = []; } public add(no) { this.children.push(no); } public getSql(sqlcommand, data) { for (const prop in this.children) { if (prop in this.children) { const noson = this.children[prop]; noson.getSql(sqlcommand, data); } } return sqlcommand; } public getValue(data, path) { for (let i = 0; typeof data === "object" && i < path.length; ++i) { if (data) { data = data[path[i]]; } } return data; } public processexpression(expression, sqlcommand, data) { let myArray; const regex = new RegExp("#{([a-z.A-Z0-9_]+)}", "ig"); while ((myArray = regex.exec(expression)) !== null) { const stretch = myArray[0]; const propertyvalue = this.getValue(data, myArray[1].split(".")); if (propertyvalue == null||typeof propertyvalue === "number"||typeof propertyvalue === "boolean"||typeof propertyvalue === "string") { expression = expression.replace(stretch, "?"); sqlcommand.addParameter(propertyvalue); } else if (util.isDate(propertyvalue)) { const value = moment(propertyvalue).format("YYYY-MM-DD HH:mm:ss"); expression = expression.replace(stretch, "?"); sqlcommand.addParameter(value); } else if (util.isArray(propertyvalue)) { throw new Error("Can not translate snippet " + stretch + " by collection: " + propertyvalue); } } return expression; } } class NoString extends No { public text; constructor(text, mapping) { super("", mapping); this.text = text.trim(); } public getSql(sqlcommand, data) { sqlcommand.sql += super.processexpression(this.text, sqlcommand, data) + " "; } } class NoChoose extends No { public noOtherwise; public children; constructor(mapping) { super("", mapping); } public add(no) { super.add(no); if (no instanceof NoOtherwise) { this.noOtherwise = no; } } public getSql(sqlcommand, data) { for (const i in this.children) { const no = this.children[i]; if (no instanceof NoWhen) { const nowhen = no; const expression = nowhen.expressionTest.replace("#{", "data.").replace("}", ""); try { eval("if( " + expression + " ) data.valueExpression = true; else data.valueExpression = false;"); } catch (err) { data.valueExpression = false; } if (data.valueExpression) { return nowhen.getSql(sqlcommand, data); } } } if (this.noOtherwise) { return this.noOtherwise.getSql(sqlcommand, data); } return ""; } } class NoWhen extends No { public expressionTest; public text; constructor(expressionTest, text, mapping) { super("", mapping); this.expressionTest = expressionTest; this.text = text; const regex = new RegExp("[_a-zA-Z][_a-zA-Z0-9]{0,30}", "ig"); const identifiers = []; let myArray = []; while ((myArray = regex.exec(expressionTest)) !== null) { const identifier = myArray[0]; if (identifier == "null" || identifier == "true" || identifier == "false" || identifier == "and") { continue; } identifiers.push(identifier); this.expressionTest = this.expressionTest.replace(identifier, "data." + identifier); } this.expressionTest = s(this.expressionTest).replaceAll("and", "&&").toString(); } } class NoForEach extends No { public item; public index; public separator; public opening; public closure; public collection; public text; constructor(item, index, separator, opening, closure, text, collection, mapping) { super("", mapping); this.item = item; this.index = index; this.separator = separator; this.opening = opening; this.closure = closure; this.collection = collection; this.text = text.trim(); } public getSql(sqlcommand, data) { const text = []; let collection = data[this.collection]; if (collection == null) { if (util.isArray(data)) { collection = data; } else { return this.opening + this.closure; } } for (const item of collection) { let myArray; const regex = new RegExp("#{([a-z.A-Z]+)}", "ig"); const expression = this.text; let newexpression = expression; while ((myArray = regex.exec(expression)) !== null) { const stretch = myArray[0]; const property = myArray[1].replace(this.item + ".", ""); const propertyvalue = this.getValue(item, property.split(".")); if (typeof propertyvalue == "number"||typeof propertyvalue == "string"||typeof propertyvalue == "boolean") { newexpression = newexpression.replace(stretch, "?"); sqlcommand.addParameter(propertyvalue); } } text.push(newexpression); } const sql = this.opening + text.join(this.separator) + this.closure; sqlcommand.sql += sql; return sqlcommand; } } class NoIf extends No { public expressionTest; public text; constructor(expressionTest, text, mapping) { super( "", mapping); this.expressionTest = expressionTest; this.text = text; const regex = new RegExp("[_a-zA-Z][_a-zA-Z0-9]{0,30}", "ig"); const identifiers = []; let myArray = []; while ((myArray = regex.exec(expressionTest)) !== null) { const identifier = myArray[0]; if (identifier == "null") { continue; } identifiers.push(identifier); this.expressionTest = this.expressionTest.replace(identifier, "data." + identifier); } } public getSql(sqlcommand, data) { const expression = this.expressionTest.replace("#{", "data.").replace("}", ""); try { if (expression) { eval("if( " + expression + " ) data.valueExpression = true; else data.valueExpression = false;"); } } catch (err) { data.valueExpression = false; } if (data.valueExpression == false) { return ""; } super.getSql(sqlcommand, data) + " "; } } class NoOtherwise extends No { public text; constructor(text, mapping) { super( "", mapping); this.text = text; } public getSql(sqlcommand, data) { let myArray; const regex = new RegExp("#{([a-z.A-Z]+)}", "ig"); let expression = this.text; while ((myArray = regex.exec(this.text)) !== null) { const stretch = myArray[0]; const propertyvalue = this.getValue(data, myArray[1].split(".")); if (typeof propertyvalue == "number"||typeof propertyvalue == "string"||typeof propertyvalue == "boolean") { expression = expression.replace(stretch, "?"); sqlcommand.addParameter(propertyvalue); } } sqlcommand.sql += expression + " "; } } class Main { public context; public pool; constructor(pool) { this.pool = pool; this.context = new Context(); } public read(name, gchild:Node&ChildNode, mapping) { const id = (<Element>gchild).getAttributeNode("id").value; const incharge = new No(id, mapping); for (const no of Array.from(gchild.childNodes)) { if (no.nodeName == "choose") { this.readChoose(no, incharge, mapping); } else if (no.nodeName == "if") { this.readIf(no, incharge, mapping); } else if (no.nodeName == "foreach") { this.readForEach(no, incharge, mapping); } else { if (no.hasChildNodes() == false) { const noString = new NoString(no.textContent, mapping); incharge.add(noString); } } } return incharge; } public readForEach(no, nomain, mapping) { let valueSeparador = ""; if (no.getAttributeNode("separator")) { valueSeparador = no.getAttributeNode("separator").value; } let valueAverage = ""; if (no.getAttributeNode("open")) { valueAverage = no.getAttributeNode("open").value; } let closingvalue = ""; if (no.getAttributeNode("close")) { closingvalue = no.getAttributeNode("close").value; } let valueIndex = ""; if (no.getAttributeNode("index")) { valueIndex = no.getAttributeNode("index").value; } let valueCollection = ""; if (no.getAttributeNode("collection")) { valueCollection = no.getAttributeNode("collection").value; } const noday = new NoForEach(no.getAttributeNode("item").value, valueIndex, valueSeparador, valueAverage, closingvalue, no.textContent, valueCollection, mapping); nomain.add(noday); } public readIf(no, nomain, mapping) { const noIf = new NoIf(no.getAttributeNode("test").value, no.childNodes[0].toString(), mapping); for (let i = 0; i < no.childNodes.length; i++) { const noson = no.childNodes[i]; if (noson.nodeName == "choose") { this.readChoose(noson, noIf, mapping); } else if (noson.nodeName == "if") { this.readIf(noson, noIf, mapping); } else if (noson.nodeName == "foreach") { this.readForEach(noson, noIf, mapping); } else { if (noson.hasChildNodes() == false) { const noString = new NoString(noson.textContent, mapping); noIf.add(noString); } } } nomain.add(noIf); } public readChoose(no, nomain, mapping) { const nohead = new NoChoose(mapping); for (const noson of no.childNodes) { if (noson.nodeName == "when") { nohead.add(this.readNoWhen( noson, no, mapping)); } else if (noson.nodeName == "otherwise") { nohead.add(new NoOtherwise(noson.childNodes[0].toString(), mapping)); } } nomain.add(nohead); } public readNoWhen( no, noPrivate, mapping) { const expressionTest = no.getAttributeNode("test").value; const nowhen = new NoWhen(expressionTest, "", mapping); for (const noson of no.childNodes) { if (noson.nodeName == "choose") { this.readChoose(noson, nowhen, mapping); } else if (noson.nodeName == "if") { this.readIf(noson, nowhen, mapping); } else if (noson.nodeName == "foreach") { this.readForEach(noson, nowhen, mapping); } else if (noson.hasChildNodes() == false) { const noString = new NoString(noson.textContent, mapping); nowhen.add(noString); } } return nowhen; } public process(dir_xml) { const mapNos = {}; const templateManager = new TemplateMapManager(this.context, this.pool); const models = {}; const stats=fs.statSync(dir_xml) if(stats.isFile()){ const mapping = this.processFile(dir_xml); templateManager.add(mapping); return templateManager; }else if(stats.isDirectory()){ const files = fs.readdirSync(dir_xml); for (const prop in files) { const archive = files[prop]; const mapping = this.processFile(path.resolve(dir_xml,archive)); templateManager.add(mapping); } return templateManager; } } private processFile(filename) { if (fs.lstatSync(filename).isDirectory()) { return null; } const xml = fs.readFileSync(filename).toString(); const xmlDoc = new DOMParser().parseFromString(xml); if (xmlDoc.documentElement.nodeName != "mapper") { return null; } const we = xmlDoc.documentElement.childNodes; const mapping = new Mapping(xmlDoc.documentElement.getAttributeNode("namespace").value); for (const noXml of Array.from(we)) { if (noXml.nodeName != "#text" && noXml.nodeName != "#comment") { const no = this.read(noXml.nodeName, noXml, mapping); mapping.add(no); } } return mapping; } } class TemplateMapManager { public mappings; public mapMapping; public context; public pool; constructor(context, pool) { this.mappings = []; this.mapMapping = {}; this.context = context; this.pool = pool; } public add(mapping) { if (mapping == null) { return; } this.mapMapping[mapping.name] = mapping; this.mappings.push(mapping); } public getNo(fullnameResultMap) { const nameNamespace = fullnameResultMap.split(".")[0]; const id = fullnameResultMap.split(".")[1]; const mapping = this.mapMapping[nameNamespace]; return mapping.getNo(id); } public insert(fullname, object) { return new Promise((resolve, reject) => { const no = this.getNo(fullname); const sqlcommand = new SqlCommand(); no.getSql(sqlcommand, object); this.connection((connection)=> { connection.query(sqlcommand.sql, sqlcommand.parameters, (err, rows, fields)=> { if (err) { reject(err); } resolve(rows.affectedRows); }); }); }); } public update(fullname, object) { return new Promise((resolve, reject) => { const no = this.getNo(fullname); const sqlcommand = new SqlCommand(); const sql = no.getSql(sqlcommand, object); this.connection((connection)=> { connection.query(sqlcommand.sql, sqlcommand.parameters, function(err, rows, fields) { if (err) { reject(err); } resolve(rows.affectedRows); }); }); }); } public remove(fullname, object) { return new Promise((resolve, reject) => { const no = this.getNo(fullname); const sqlcommand = new SqlCommand(); const sql = no.getSql(sqlcommand, object); this.connection((connection) => { connection.query(sqlcommand.sql, sqlcommand.parameters, (err, rows, fields) => { if (err) { reject(err); } resolve(rows.affectedRows); }); }); }); } public async selectOne(fullname, data) { const objects=await this.selectList(fullname, data) if ((objects as any[]).length == 1) { return objects[0]; } return null; } public selectList(fullname, data) { return new Promise((resolve, reject) => { const no = this.getNo(fullname); const sqlcommand = new SqlCommand(); no.getSql(sqlcommand, data); this.connection(connection=> { connection.query(sqlcommand.sql, sqlcommand.parameters, (err, rows, fields)=> { if (err) { reject(err); } resolve(rows); }); }); }); } public connection(callback) { return this.context.getConnected(callback, this.pool); } public transaction(callback) { return this.context.initiationTransaction(callback, this.pool); } } class Mapping { public name; public children; public nosPorId; constructor(name) { this.name = name; this.children = []; this.nosPorId = {}; } public add(noson) { this.children.push(noson); this.nosPorId[noson.id] = noson; } public getNo(id) { return this.nosPorId[id]; } } export default Main;