def __init__(self): from db import ConnectionManager self.connection_manager = ConnectionManager(XConfig.get('db')) self.cache_manager = CacheManager(XConfig.get('cache')) self.entity_list = {} self.use_cache = False self.use_preload = True self.use_validator = False self.bad_entitys = []
class UnitOfWork(object): ''' 工作单元 @note: 由于数据库连接线程安全的限制,工作单元只提供thread local的访问实例,不提供进程级实例 ''' def __init__(self): from db import ConnectionManager self.connection_manager = ConnectionManager(XConfig.get('db')) self.cache_manager = CacheManager(XConfig.get('cache')) self.entity_list = {} self.use_cache = False self.use_preload = True self.use_validator = False self.bad_entitys = [] def idgenerator(self): if not hasattr(self, '_idgenerator') or not self._idgenerator: connection = self.connection_manager.get( XConfig.get('idgenerator.db')) self._idgenerator = IdGenerator( connection, XConfig.get('idgenerator.count') or 5) return self._idgenerator def register(self, entity): '''注册实体到工作单元 ''' cls_name = entity.__class__.__name__ if self.entity_list.get(cls_name) is None: self.entity_list[cls_name] = {} self.entity_list[cls_name][str(entity.getId())] = entity entity._unitofwork = self def commit(self): ''' ''' deletes = [] updates = [] news = [] db_names = set() for entity_class_name in self.entity_list.keys(): entity_dict = self.entity_list.get(entity_class_name) for entity_id in entity_dict.keys(): entity = entity_dict.get(entity_id) if entity.isDelete(): entity.onDelete() elif entity.isNew(): entity.onNew() elif entity.isDirty(): entity.onUpdate() else: continue self.bad_entitys = [] for entity_class_name in self.entity_list.keys(): entity_dict = self.entity_list.get(entity_class_name) for entity_id in entity_dict.keys(): entity = entity_dict.get(entity_id) if entity.isDelete(): deletes.append(entity) elif entity.isNew(): news.append(entity) elif entity.isDirty(): updates.append(entity) else: continue if entity.isLoadedFromCache(): raise ModifyBasedCacheError( "%s(%s) is loaded from cache, so can't be modified!!" % (entity.__class__.__name__, entity.id)) if self.use_validator and not entity.doValidate(): self.bad_entitys.append(entity) db_names.add(entity._db) if self.use_validator and self.bad_entitys: return False for name in db_names: connection = self.connection_manager.get(name) if connection and name == connection.name: connection.begin() try: for entitys in [deletes, updates, news]: for entity in entitys: self.sync(entity) for name in db_names: connection = self.connection_manager.get(name) if name == connection.name: connection.commit() for entity in deletes: try: cache = self.cache_manager.get(entity._cache) if not cache: continue cache_key = self.makeKey(entity.__class__, entity.id) cache.delete(cache_key) except: logging.exception("delete cache fail") for entitys in [updates, news]: for entity in entitys: try: cache = self.cache_manager.get(entity._cache) if not cache: continue cache_key = self.makeKey(entity.__class__, entity.id) cache.set(cache_key, entity.getCacheDict()) except: logging.exception("set cache fail") return True except: logging.exception("[XWEB] COMMIT FAILED, ROLLBACK") for name in db_names: connection = self.connection_manager.get(name) if name == connection.name: connection.rollback() return False finally: self.entity_list.clear() def getEntityInMemory(self, cls, entity_id): cls_name = cls.__name__ if self.entity_list.get(cls_name) is None: return None return self.entity_list.get(cls_name).get(str(entity_id)) def getList(self, cls, entity_ids, **kwargs): db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) query_db_ids = [] query_cache_ids = [] for entity_id in entity_ids: entity = self.getEntityInMemory(cls, entity_id) if not entity: query_cache_ids.append(entity_id) if query_cache_ids: if self.use_cache: keys = [ self.makeKey(cls, entity_id) for entity_id in query_cache_ids ] cache_name = cls._cache_name cache = self.cache_manager.get(cache_name) if not cache: raise ValueError( 'CACHE DOES NOT EXSITS WHEN USE_CACHE IS TRUE') entitys = cache.getList(keys) for entity_id, key in zip(query_cache_ids, keys): cache_dict = entitys.get(key) if cache_dict: entity = cls(**cache_dict) entity._is_new = False entity._is_delete = False entity._is_dirty = False entity._load_from_cache = True entity._db = 'default' entity._cache = cache_name self.register(entity) logging.debug("[XWEB] LOAD ENTITY %s FROM CACHE: %s" % (entity, cache_name)) else: query_db_ids.append(entity_id) else: query_db_ids = query_cache_ids entitys = connection.getEntityList(cls, query_db_ids) if not entitys: return [] first_entity = entitys[0] first_entity.setProps('entity_ids_in_query', entity_ids) for entity in entitys: self.register(entity) entity.setProps('first_entity_in_query', first_entity.id) logging.debug("[XWEB] LOAD ENTITY %s FROM DB: %s" % (entity, db_conn)) return [ self.getEntityInMemory(cls, entity_id) for entity_id in entity_ids if self.getEntityInMemory(cls, entity_id) ] def getListByCond(self, criteria, **kwargs): if not isinstance(criteria, QueryCriteria): return [] cls = criteria.entity_cls db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) entity_ids = connection.fetchEntityIds(criteria) return self.getList(cls, entity_ids, **kwargs) def fetchRowsByCond(self, cr, **kwargs): cls = cr.entity_cls db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) return connection.fetchRowsByCond(cr) def fetchRowByCond(self, cr, **kwargs): cls = cr.entity_cls db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) return connection.fetchRowByCond(cr) def getListByCond2(self, cls, condition=None, args=[], **kwargs): db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) rows = connection.queryRowsByCond(cls, condition, args) results = [] for row in rows: data = {} for k, v in zip(cls.allKeys(), row): data[k] = v entity_id = tuple([data.get(k) for k in cls.primaryKey()]) entity = self.getEntityInMemory(cls, entity_id) if not entity: entity = connection.createEntity(cls, row) self.register(entity) key = self.makeKey(cls, entity_id) cache_name = cls._cache_name cache = self.cache_manager.get(cache_name) if cache: cache.set(key, entity.getCacheDict()) results.append(entity) return results def get(self, cls, entity_id, **kwargs): #@ReservedAssignment entity = self.getEntityInMemory(cls, entity_id) if entity: return entity key = self.makeKey(cls, entity_id) cache_name = cls._cache_name cache = self.cache_manager.get(cache_name) if self.use_cache and cache: cache_dict = cache.get(key) if cache_dict: entity = cls(**cache_dict) entity._is_new = False entity._is_delete = False entity._is_dirty = False entity._load_from_cache = True entity._db = 'default' entity._cache = cache_name self.register(entity) logging.debug("[XWEB] LOAD ENTITY %s FROM CACHE: %s" % (entity, cache_name)) return entity db_conn = cls.dbName(entity_id=entity_id, **kwargs) connection = self.connection_manager.get(db_conn) entity = connection.getEntity(cls, entity_id) if entity is None: return None self.register(entity) if cache: cache.set(key, entity.getCacheDict()) logging.debug("[XWEB] LOAD ENTITY %s FROM DB: %s" % (entity, db_conn)) return entity def sync(self, entity): connection = self.connection_manager.get(entity._db) if entity.isNew(): if connection.insert(entity): entity._is_dirty = False entity._is_new = False entity.is_delete = False return True elif entity.isDelete(): if connection.delete(entity): entity._is_dirty = False entity._is_new = False entity.is_delete = True return True elif entity.isDirty(): if connection.update(entity): entity._is_dirty = False entity._is_new = False entity.is_delete = False return True else: raise EntityStatusError() return False def makeKey(self, cls, entity_id): return "%s:%s:%s:%s" % (XConfig.get('app_name'), cls.__name__, entity_id, cls._version) def close(self): self.connection_manager.close() # static method @classmethod def inst(cls): thread = threading.currentThread() if not hasattr(thread, 'unitofwork') or not thread.unitofwork: thread.unitofwork = UnitOfWork() return thread.unitofwork @classmethod def Commit(cls): cls.inst().commit() @classmethod def Close(cls): cls.inst().close() @classmethod def Reset(cls, force=False): thread = threading.currentThread() if hasattr(thread, 'unitofwork') and thread.unitofwork: if not force: unitofwork = thread.unitofwork unitofwork.entity_list = {} unitofwork.bad_entitys = {} unitofwork.use_cache = True unitofwork.use_preload = False else: del thread.unitofwork @classmethod def reset(cls, force=False): cls.Reset(force)
class UnitOfWork(object): ''' 工作单元 @note: 由于数据库连接线程安全的限制,工作单元只提供thread local的访问实例,不提供进程级实例 ''' def __init__(self): from db import ConnectionManager self.connection_manager = ConnectionManager(XConfig.get('db')) self.cache_manager = CacheManager(XConfig.get('cache')) self.entity_list = {} self.use_cache = False self.use_preload = True self.use_validator = False self.bad_entitys = [] def idgenerator(self): if not hasattr(self, '_idgenerator') or not self._idgenerator: connection = self.connection_manager.get(XConfig.get('idgenerator.db')) self._idgenerator = IdGenerator(connection, XConfig.get('idgenerator.count') or 5) return self._idgenerator def register(self, entity): '''注册实体到工作单元 ''' cls_name = entity.__class__.__name__ if self.entity_list.get(cls_name) is None: self.entity_list[cls_name] = {} self.entity_list[cls_name][str(entity.getId())] = entity entity._unitofwork = self def commit(self): ''' ''' deletes = [] updates = [] news = [] db_names = set() for entity_class_name in self.entity_list.keys(): entity_dict = self.entity_list.get(entity_class_name) for entity_id in entity_dict.keys(): entity = entity_dict.get(entity_id) if entity.isDelete(): entity.onDelete() elif entity.isNew(): entity.onNew() elif entity.isDirty(): entity.onUpdate() else: continue self.bad_entitys = [] for entity_class_name in self.entity_list.keys(): entity_dict = self.entity_list.get(entity_class_name) for entity_id in entity_dict.keys(): entity = entity_dict.get(entity_id) if entity.isDelete(): deletes.append(entity) elif entity.isNew(): news.append(entity) elif entity.isDirty(): updates.append(entity) else: continue if entity.isLoadedFromCache(): raise ModifyBasedCacheError("%s(%s) is loaded from cache, so can't be modified!!"%( entity.__class__.__name__, entity.id)) if self.use_validator and not entity.doValidate(): self.bad_entitys.append(entity) db_names.add(entity._db) if self.use_validator and self.bad_entitys: return False for name in db_names: connection = self.connection_manager.get(name) if connection and name == connection.name: connection.begin() try: for entitys in [deletes, updates, news]: for entity in entitys: self.sync(entity) for name in db_names: connection = self.connection_manager.get(name) if name == connection.name: connection.commit() for entity in deletes: try: cache = self.cache_manager.get(entity._cache) if not cache: continue cache_key = self.makeKey(entity.__class__, entity.id) cache.delete(cache_key) except: logging.exception("delete cache fail") for entitys in [updates, news]: for entity in entitys: try: cache = self.cache_manager.get(entity._cache) if not cache: continue cache_key = self.makeKey(entity.__class__, entity.id) cache.set(cache_key, entity.getCacheDict()) except: logging.exception("set cache fail") return True except: logging.exception("[XWEB] COMMIT FAILED, ROLLBACK") for name in db_names: connection = self.connection_manager.get(name) if name == connection.name: connection.rollback() return False finally: self.entity_list.clear() def getEntityInMemory(self, cls, entity_id): cls_name = cls.__name__ if self.entity_list.get(cls_name) is None: return None return self.entity_list.get(cls_name).get(str(entity_id)) def getList(self, cls, entity_ids, **kwargs): db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) query_db_ids = [] query_cache_ids = [] for entity_id in entity_ids: entity = self.getEntityInMemory(cls, entity_id) if not entity: query_cache_ids.append(entity_id) if query_cache_ids: if self.use_cache: keys = [self.makeKey(cls, entity_id) for entity_id in query_cache_ids] cache_name = cls._cache_name cache = self.cache_manager.get(cache_name) if not cache: raise ValueError('CACHE DOES NOT EXSITS WHEN USE_CACHE IS TRUE') entitys = cache.getList(keys) for entity_id, key in zip(query_cache_ids, keys): cache_dict = entitys.get(key) if cache_dict: entity = cls(**cache_dict) entity._is_new = False entity._is_delete = False entity._is_dirty = False entity._load_from_cache = True entity._db = 'default' entity._cache = cache_name self.register(entity) logging.debug("[XWEB] LOAD ENTITY %s FROM CACHE: %s"%(entity, cache_name)) else: query_db_ids.append(entity_id) else: query_db_ids = query_cache_ids entitys = connection.getEntityList(cls, query_db_ids) if not entitys: return [] first_entity = entitys[0] first_entity.setProps('entity_ids_in_query', entity_ids) for entity in entitys: self.register(entity) entity.setProps('first_entity_in_query', first_entity.id) logging.debug("[XWEB] LOAD ENTITY %s FROM DB: %s"%(entity, db_conn)) return [self.getEntityInMemory(cls, entity_id) for entity_id in entity_ids if self.getEntityInMemory(cls, entity_id)] def getListByCond(self, criteria, **kwargs): if not isinstance(criteria, QueryCriteria): return [] cls = criteria.entity_cls db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) entity_ids = connection.fetchEntityIds(criteria) return self.getList(cls, entity_ids, **kwargs) def fetchRowsByCond(self, cr, **kwargs): cls = cr.entity_cls db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) return connection.fetchRowsByCond(cr) def fetchRowByCond(self, cr, **kwargs): cls = cr.entity_cls db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) return connection.fetchRowByCond(cr) def getListByCond2(self, cls, condition=None, args=[], **kwargs): db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) rows = connection.queryRowsByCond(cls, condition, args) results = [] for row in rows: data = {} for k,v in zip(cls.allKeys(), row): data[k] = v entity_id = tuple([data.get(k) for k in cls.primaryKey()]) entity = self.getEntityInMemory(cls, entity_id) if not entity: entity = connection.createEntity(cls, row) self.register(entity) key = self.makeKey(cls, entity_id) cache_name = cls._cache_name cache = self.cache_manager.get(cache_name) if cache: cache.set(key, entity.getCacheDict()) results.append(entity) return results def get(self, cls, entity_id, **kwargs): #@ReservedAssignment entity = self.getEntityInMemory(cls, entity_id) if entity: return entity key = self.makeKey(cls, entity_id) cache_name = cls._cache_name cache = self.cache_manager.get(cache_name) if self.use_cache and cache: cache_dict = cache.get(key) if cache_dict: entity = cls(**cache_dict) entity._is_new = False entity._is_delete = False entity._is_dirty = False entity._load_from_cache = True entity._db = 'default' entity._cache = cache_name self.register(entity) logging.debug("[XWEB] LOAD ENTITY %s FROM CACHE: %s"%(entity, cache_name)) return entity db_conn = cls.dbName(entity_id=entity_id, **kwargs) connection = self.connection_manager.get(db_conn) entity = connection.getEntity(cls, entity_id) if entity is None: return None self.register(entity) if cache: cache.set(key, entity.getCacheDict()) logging.debug("[XWEB] LOAD ENTITY %s FROM DB: %s"%(entity, db_conn)) return entity def sync(self, entity): connection = self.connection_manager.get(entity._db) if entity.isNew(): if connection.insert(entity): entity._is_dirty = False entity._is_new = False entity.is_delete = False return True elif entity.isDelete(): if connection.delete(entity): entity._is_dirty = False entity._is_new = False entity.is_delete = True return True elif entity.isDirty(): if connection.update(entity): entity._is_dirty = False entity._is_new = False entity.is_delete = False return True else: raise EntityStatusError() return False def makeKey(self, cls, entity_id): return "%s:%s:%s:%s"%(XConfig.get('app_name'), cls.__name__, entity_id, cls._version) def close(self): self.connection_manager.close() # static method @classmethod def inst(cls): thread = threading.currentThread() if not hasattr(thread, 'unitofwork') or not thread.unitofwork: thread.unitofwork = UnitOfWork() return thread.unitofwork @classmethod def Commit(cls): cls.inst().commit() @classmethod def Close(cls): cls.inst().close() @classmethod def Reset(cls, force=False): thread = threading.currentThread() if hasattr(thread, 'unitofwork') and thread.unitofwork: if not force: unitofwork = thread.unitofwork unitofwork.entity_list = {} unitofwork.bad_entitys = {} unitofwork.use_cache = True unitofwork.use_preload = False else: del thread.unitofwork @classmethod def reset(cls, force=False): cls.Reset(force)