local ModelBase = class("ModelBase") ModelBase.key = "key" ModelBase.schema = {} local string_format = string.format local table_insert = table.insert local table_unpack = table.unpack local assert = assert local next = next local ipairs = ipairs local pairs = pairs local tostring = tostring local tonumber = tonumber local redisproxy = redisproxy local function filterProperties(properties, filter) for i, field in ipairs(filter) do properties[field] = nil end end function ModelBase:ctor(properties) self.isModelBase_ = true self.cacheFields = {} --缓存字段 不更新数据库的字段 self[self.class.key .. "_"] = properties[self.class.key] --数据库key properties[self.class.key] = nil if not self:isValidKey() then print(string_format("%s [%s:key] should be give in new(ctor)", tostring(self), self.class.__cname)) return end if type(properties) ~= "table" then properties = {} end self:loadProperties(properties) --缺少的域将设置默认值 end -- startCache 和 endCache 在恰当的时候*配对使用* 嵌套使用多次增加引用计数 直到引用计数为0 写入 function ModelBase:startCache( ... ) for _, field in ipairs({ ... }) do if self.class.schema[field] then self.cacheFields[field] = (self.cacheFields[field] or 0) + 1 end end end --减少缓存引用计数 为时写入, 无参数 强制刷新所有缓存 function ModelBase:endCache( ... ) local args = { ... } local params = {} local function doOneCache(field) local propname = field .. "_" table_insert(params, field) if self.class.schema[field][1] == "table" then table_insert(params, MsgPack.pack(self[propname])) else table_insert(params, self[propname]) end end if not next(args) then for field, _ in pairs(self.cacheFields) do doOneCache(field) end self.cacheFields = {} else for _, field in ipairs(args) do if self.cacheFields[field] then self.cacheFields[field] = self.cacheFields[field] - 1 if self.cacheFields[field] <= 0 then self.cacheFields[field] = nil doOneCache(field) end end end end if next(params) then redisproxy:hmset(self:getKey(), table_unpack(params)) end end --[[-- 返回对象的 ID 值。 **Returns:** - ID 值 ]] function ModelBase:getKey() local id = self[self.class.key .. "_"] assert(id ~= nil, string_format("%s [%s:getKey()] Invalid key", tostring(self), self.class.__cname)) return id end function ModelBase:load(properties) if not self:isValidKey() then print(string_format("%s [%s:id] should be set before load", tostring(self), self.class.__cname)) return false end if not properties then properties = redisproxy:hgetall(self:getKey()) properties = table.arrayToMap(properties) end if not next(properties) then return false end self:loadProperties(properties) self:onLoad() return true end --创建model对应的redis数据, 必须已经设置了ID function ModelBase:create() if not self:isValidKey() then print(string_format("%s [%s:key] should be set before create", tostring(self), self.class.__cname)) return nil end self:save() self:onCreate() return self end -- save 忽略 缓存配置 function ModelBase:save() local redisProperties = self:getProperties() local params = {} for fieldName, value in pairs(redisProperties) do local propname = fieldName .. "_" table_insert(params, fieldName) if self.class.schema[fieldName][1] == "table" then table_insert(params, MsgPack.pack(self[propname])) else table_insert(params, self[propname]) end end if next(params) then redisproxy:hmset(self:getKey(), table_unpack(params)) end end --[[-- 确定对象是否设置了有效的 key。 ]] function ModelBase:isValidKey() local propname = self.class.key .. "_" local key = self[propname] return type(key) == "string" and key ~= "" end --[[-- 加载对象的属性进内存。 NOTE: 如果properties缺少schema中的域, 将用默认值来填充 **Parameters:** - properties: 包含属性值的数组 ]] function ModelBase:loadProperties(properties) assert(type(properties) == "table", "Invalid properties") for field, schema in pairs(self.class.schema) do local typ, def = table_unpack(schema) local propname = field .. "_" if typ == "table" and type(properties[field]) == "string" then properties[field] = MsgPack.unpack(properties[field]) end local val = properties[field] or def if val ~= nil then if typ == "number" then val = tonumber(val) end assert(type(val) == typ, string_format("%s [%s:setProperties()] Type mismatch, %s expected %s, actual is %s", tostring(self), self.class.__cname, field, typ, type(val))) self[propname] = val end end end --[[-- 取得对象的属性值。 **Parameters:** - fields: 要取得哪些属性的值,如果未指定该参数,则返回 fields 中设定的属性 - filter: 要从结果中过滤掉哪些属性,如果未指定则不过滤 **Returns:** - 包含属性值的数组 ]] function ModelBase:getProperties(fields, filter) local schema = self.class.schema if type(fields) ~= "table" then fields = table.keys(self.class.schema) end local properties = {} for i, field in ipairs(fields) do local propname = field .. "_" local typ = schema[field][1] local val = self[propname] assert(type(val) == typ, string_format("%s [%s:getProperties()] Type mismatch, %s expected %s, actual is %s", tostring(self), self.class.__cname, field, typ, type(val))) properties[field] = val end if type(filter) == "table" then filterProperties(properties, filter) end return properties end function ModelBase:getProperty(property) if type(property) ~= "string" then return nil end if not self.class.schema[property] then return nil end return self:getProperties({property})[property] end function ModelBase:setProperty(property, value) if not self.class.schema[property] then print(string_format("%s [%s:setProperty()] Invalid property : %s", tostring(self), self.class.__cname, property)) return end local typ, def = table_unpack(self.class.schema[property]) local propname = property .. "_" if typ == "number" then value = tonumber(value) end if typ == "table" and not value then value = self[propname] -- table 可以用自己的缓冲 end assert(type(value) == typ, string_format("%s [%s:setProperties()] Type mismatch, %s expected %s, actual is %s", tostring(self), self.class.__cname, property, typ, type(value))) self[propname] = value if not self.cacheFields[property] then -- table 使用msgpack if typ == "table" then value = MsgPack.pack(value) end redisproxy:hset(self:getKey(), property, value) end end function ModelBase:setProperties(fields) local result = {} for property, value in pairs(fields) do if not self.class.schema[property] then print(string_format("%s [%s:setProperty()] Invalid property : %s", tostring(self), self.class.__cname, property)) else local typ, def = table_unpack(self.class.schema[property]) local propname = property .. "_" if typ == "number" then value = tonumber(value) end if typ == "table" and not value then value = self[propname] -- table 可以用自己的缓冲 end assert(type(value) == typ, string_format("%s [%s:setProperties()] Type mismatch, %s expected %s, actual is %s", tostring(self), self.class.__cname, property, typ, type(value))) self[propname] = value if not self.cacheFields[property] then table_insert(result, property) if typ == "table" then table_insert(result, MsgPack.pack(self[propname])) else table_insert(result, self[propname]) end end end end if next(result) then redisproxy:hmset(self:getKey(), table_unpack(result)) end end function ModelBase:incrProperty(property, value) if not self.class.schema[property] then print(string_format("%s [%s:setProperty()] Invalid property : %s", tostring(self), self.class.__cname, property)) return end local typ, def = table_unpack(self.class.schema[property]) local propname = property .. "_" if typ == "table" then return end if typ == "number" then value = tonumber(value) end assert(type(value) == typ, string_format("%s [%s:setProperties()] Type mismatch, %s expected %s, actual is %s", tostring(self), self.class.__cname, property, typ, type(value))) self[propname] = self[propname] + value if not self.cacheFields[property] then return redisproxy:hincrby(self:getKey(), property, value) end end function ModelBase:onLoad() end function ModelBase:onCreate() end return ModelBase