From 1c70310df90ba05cfd4f41a7252ecf032e77c8b4 Mon Sep 17 00:00:00 2001 From: aiteasoft Date: Sun, 20 Apr 2025 02:51:16 +0800 Subject: [PATCH 1/2] update for V1.6.0, condition,cache,result/param transform and so on --- src/bee/api.py | 287 ++++++++++----- src/bee/condition.py | 329 ++++++++++++----- src/bee/config.py | 216 ++++++++--- src/bee/conn_builder.py | 52 +-- src/bee/context.py | 146 ++++++-- src/bee/custom.py | 12 + src/bee/exception.py | 3 + src/bee/factory.py | 24 +- src/bee/gen.py | 126 +++++++ src/bee/helper.py | 12 + src/bee/honeyfactory.py | 10 +- src/bee/name/NameCheckUtil.py | 14 +- src/bee/name/NameUtil.py | 28 +- src/bee/name/naming.py | 33 +- src/bee/name/naming_handler.py | 2 +- src/bee/{ => osql}/SqlUtil.py | 34 +- src/bee/osql/Util.py | 48 +++ src/bee/{ => osql}/base.py | 80 ++-- src/bee/osql/{enum.py => bee_enum.py} | 29 +- src/bee/osql/cache.py | 404 ++++++++++++++++++++ src/bee/osql/const.py | 16 +- src/bee/osql/index.py | 106 ++++++ src/bee/osql/local.py | 37 ++ src/bee/osql/mid.py | 180 +++++++++ src/bee/osql/mid_typing.py | 104 ++++++ src/bee/{ => osql}/obj2sql.py | 354 +++++++++--------- src/bee/{ => osql}/paging.py | 13 +- src/bee/osql/sqlkeyword.py | 110 +++--- src/bee/{ => osql}/sqllib.py | 66 +++- src/bee/osql/struct.py | 38 ++ src/bee/osql/transform.py | 94 +++++ src/bee/osql/type_transform.py | 512 ++++++++++++++++++++++++++ src/bee/typing.py | 4 + src/bee/util.py | 239 +++++++++--- src/bee/version.py | 3 +- 35 files changed, 3075 insertions(+), 690 deletions(-) create mode 100644 src/bee/custom.py create mode 100644 src/bee/gen.py create mode 100644 src/bee/helper.py rename src/bee/{ => osql}/SqlUtil.py (50%) create mode 100644 src/bee/osql/Util.py rename src/bee/{ => osql}/base.py (34%) rename src/bee/osql/{enum.py => bee_enum.py} (72%) create mode 100644 src/bee/osql/cache.py create mode 100644 src/bee/osql/index.py create mode 100644 src/bee/osql/local.py create mode 100644 src/bee/osql/mid.py create mode 100644 src/bee/osql/mid_typing.py rename src/bee/{ => osql}/obj2sql.py (64%) rename src/bee/{ => osql}/paging.py (75%) rename src/bee/{ => osql}/sqllib.py (57%) create mode 100644 src/bee/osql/struct.py create mode 100644 src/bee/osql/transform.py create mode 100644 src/bee/osql/type_transform.py create mode 100644 src/bee/typing.py diff --git a/src/bee/api.py b/src/bee/api.py index 958cfd8..0f94b9b 100644 --- a/src/bee/api.py +++ b/src/bee/api.py @@ -1,12 +1,15 @@ -from bee import SqlUtil -from bee.base import AbstractCommOperate +from typing import overload + +from bee.context import HoneyContext from bee.exception import BeeException, ParamBeeException -from bee.obj2sql import ObjToSQL -from bee.osql.enum import FunctionType, SuidType -from bee.osql.logger import Logger -from bee.sqllib import BeeSql +from bee.osql import SqlUtil -from bee.condition import Condition +from bee.condition import Condition, ConditionImpl +from bee.osql.base import AbstractCommOperate +from bee.osql.bee_enum import FunctionType, SuidType, LocalType +from bee.osql.obj2sql import ObjToSQL +from bee.osql.sqllib import BeeSql +from bee.osql.struct import CacheSuidStruct class Suid(AbstractCommOperate): @@ -16,16 +19,26 @@ class Suid(AbstractCommOperate): self._beeSql = None self._objToSQL = None - def select(self, entity): - if entity is None: - return None - + @overload + def select(self, entity): + ... + + @overload + def select(self, entity, condition: Condition): + ... + + def __select(self, entity): + list_r = None try: super().doBeforePasreEntity(entity, SuidType.SELECT) - sql, params = self.objToSQL.toSelectSQL(entity) - Logger.logsql("select SQL:", sql) + sql, params, table_name = self.objToSQL.toSelectSQL(entity) + + entityClass = self._to_class_t(entity) + self._reg_cache_in_context(sql, params, table_name, "list", entityClass) + + super().logsql("select SQL:", sql) super().log_params(params) - list_r = self.beeSql.select(sql, self.to_class_t(entity), params) + list_r = self.beeSql.select(sql, entityClass, params) return list_r except Exception as e: raise BeeException(e) @@ -33,13 +46,17 @@ class Suid(AbstractCommOperate): super().doBeforeReturn(list_r) def update(self, entity): - if entity is None: + if not entity: return None try: super().doBeforePasreEntity(entity, SuidType.UPDATE) - sql, params = self.objToSQL.toUpdateSQL(entity) - Logger.logsql("update SQL:", sql) + sql, params, table_name = self.objToSQL.toUpdateSQL(entity) + + entityClass = self._to_class_t(entity) + self._reg_cache_in_context2(sql, params, table_name, "int", entityClass, SuidType.UPDATE) + + super().logsql("update SQL:", sql) super().log_params(params) return self.beeSql.modify(sql, params) except Exception as e: @@ -48,28 +65,41 @@ class Suid(AbstractCommOperate): super().doBeforeReturnSimple() def insert(self, entity): - if entity is None: + if not entity: return None try: super().doBeforePasreEntity(entity, SuidType.INSERT) - sql, params = self.objToSQL.toInsertSQL(entity) - Logger.logsql("insert SQL:", sql) + sql, params, table_name = self.objToSQL.toInsertSQL(entity) + + entityClass = self._to_class_t(entity) + self._reg_cache_in_context2(sql, params, table_name, "int", entityClass, SuidType.INSERT) + + super().logsql("insert SQL:", sql) super().log_params(params) return self.beeSql.modify(sql, params) except Exception as e: raise BeeException(e) finally: super().doBeforeReturnSimple() + + @overload + def delete(self, entity): + ... - def delete(self, entity): - if entity is None: - return None + @overload + def delete(self, entity, condition: Condition): + ... + def __delete(self, entity): try: super().doBeforePasreEntity(entity, SuidType.DELETE) - sql, params = self.objToSQL.toDeleteSQL(entity) - Logger.logsql("delete SQL:", sql) + sql, params, table_name = self.objToSQL.toDeleteSQL(entity) + + entityClass = self._to_class_t(entity) + self._reg_cache_in_context2(sql, params, table_name, "int", entityClass, SuidType.DELETE) + + super().logsql("delete SQL:", sql) super().log_params(params) return self.beeSql.modify(sql, params) except Exception as e: @@ -78,16 +108,24 @@ class Suid(AbstractCommOperate): super().doBeforeReturnSimple() # since 1.6.0 - def select2(self, entity, condition: Condition=None): - if entity is None: - return None - + def select(self, entity, condition: Condition = None): + if not entity: + return None + + if not condition: + return self.__select(entity) + + list_r = None try: super().doBeforePasreEntity(entity, SuidType.SELECT) - sql, params = self.objToSQL.toSelectSQL2(entity, condition) - Logger.logsql("select SQL:", sql) + sql, params, table_name = self.objToSQL.toSelectSQL2(entity, condition) + + entityClass = self._to_class_t(entity) + self._reg_cache_in_context(sql, params, table_name, "list", entityClass) + + super().logsql("select SQL:", sql) super().log_params(params) - list_r = self.beeSql.select(sql, self.to_class_t(entity), params) + list_r = self.beeSql.select(sql, entityClass, params) return list_r except Exception as e: raise BeeException(e) @@ -95,14 +133,21 @@ class Suid(AbstractCommOperate): super().doBeforeReturn(list_r) # since 1.6.0 - def delete2(self, entity, condition: Condition=None): - if entity is None: + def delete(self, entity, condition: Condition = None): + if not entity: return None + if not condition: + return self.__delete(entity) + try: super().doBeforePasreEntity(entity, SuidType.DELETE) - sql, params = self.objToSQL.toDeleteSQL2(entity, condition) - Logger.logsql("delete SQL:", sql) + sql, params, table_name = self.objToSQL.toDeleteSQL2(entity, condition) + + entityClass = self._to_class_t(entity) + self._reg_cache_in_context2(sql, params, table_name, "int", entityClass, SuidType.DELETE) + + super().logsql("delete SQL:", sql) super().log_params(params) return self.beeSql.modify(sql, params) except Exception as e: @@ -110,14 +155,18 @@ class Suid(AbstractCommOperate): finally: super().doBeforeReturnSimple() - def to_class_t(self, entity): - return type(entity) # 返回实体的类型 + def _to_class_t(self, entity): + return type(entity) # 返回实体的类型 - # def __init__(self, beeSql=None, objToSQL=None): - # self._beeSql = beeSql - # self._objToSQL = objToSQL - - @property + def _reg_cache_in_context(self, sql, params, table_name, returnType, entityClass): + + HoneyContext._set_data(LocalType.CacheSuidStruct, sql, CacheSuidStruct(sql, params, table_name, returnType, entityClass, SuidType.SELECT)) + + def _reg_cache_in_context2(self, sql, params, table_name, returnType, entityClass, suidType): + + HoneyContext._set_data(LocalType.CacheSuidStruct, sql, CacheSuidStruct(sql, params, table_name, returnType, entityClass, suidType)) + + @property def beeSql(self): if self._beeSql is None: self._beeSql = BeeSql() @@ -140,16 +189,38 @@ class Suid(AbstractCommOperate): class SuidRich(Suid): + @overload def select_paging(self, entity, start, size): - if entity is None: + ... + + @overload + def select_paging(self, entity, start, size, *selectFields): + ... + + def select_paging(self, entity, start, size, *selectFields): + + if not selectFields: + return self.__select_paging(entity, start, size) + + condition = ConditionImpl() + condition.selectField(*selectFields) + condition.start(start).size(size) + return super().select(entity, condition) + + def __select_paging(self, entity, start, size): + if not entity: return None try: super().doBeforePasreEntity(entity, SuidType.SELECT) - sql, params = self.objToSQL.toSelectSQLWithPaging(entity, start, size) - Logger.logsql("select_paging SQL:", sql) + sql, params, table_name = self.objToSQL.toSelectSQLWithPaging(entity, start, size) + + entityClass = self._to_class_t(entity) + self._reg_cache_in_context(sql, params, table_name, "list", entityClass) + + super().logsql("select_paging SQL:", sql) super().log_params(params) - list_r = self.beeSql.select(sql, self.to_class_t(entity), params) + list_r = self.beeSql.select(sql, entityClass, params) return list_r except Exception as e: raise BeeException(e) @@ -157,16 +228,20 @@ class SuidRich(Suid): super().doBeforeReturn(list_r) def insert_batch(self, entity_list): - if entity_list is None: + if not entity_list: return None if len(entity_list) == 0: return 0 try: super().doBeforePasreListEntity(entity_list, SuidType.INSERT) - sql, list_params = self.objToSQL.toInsertBatchSQL(entity_list) - Logger.logsql("insert batch SQL:", sql) - super().log_params(list_params) + sql, list_params, table_name = self.objToSQL.toInsertBatchSQL(entity_list) + + entityClass = self._to_class_t(entity_list[0]) + self._reg_cache_in_context2(sql, list_params, table_name, "int", entityClass, SuidType.INSERT) + + super().logsql("insert batch SQL:", sql) + super().log_params_for_batch(list_params) return self.beeSql.batch(sql, list_params) except Exception as e: raise BeeException(e) @@ -174,8 +249,15 @@ class SuidRich(Suid): super().doBeforeReturnSimple() def select_first(self, entity): - # listT = self.select_paging(entity, 0, 2) - listT = self.select_paging(entity, 0, 1) + + start = 0 + size = 2 + if HoneyContext.isMySql() or HoneyContext.isSQLite(): + size = 1 + elif HoneyContext.isOracle(): + start = 1 + + listT = self.select_paging(entity, start, size) if listT: # 判断列表是否非空 return listT[0] # 返回首个元素 return None @@ -192,8 +274,11 @@ class SuidRich(Suid): try: id_list = list(ids) super().doBeforePasreEntity(entity_class, SuidType.SELECT) - sql = self.objToSQL.toSelectByIdSQL(entity_class, len(id_list)) - Logger.logsql("select by id SQL:", sql) + sql, table_name = self.objToSQL.toSelectByIdSQL(entity_class, len(id_list)) + + self._reg_cache_in_context(sql, id_list, table_name, "", entity_class) + + super().logsql("select by id SQL:", sql) super().log_params(id_list) return self.beeSql.select(sql, entity_class, id_list) except Exception as e: @@ -212,8 +297,11 @@ class SuidRich(Suid): try: id_list = list(ids) super().doBeforePasreEntity(entity_class, SuidType.DELETE) - sql = self.objToSQL.toDeleteById(entity_class, len(id_list)) - Logger.logsql("delete by id SQL:", sql) + sql, table_name = self.objToSQL.toDeleteById(entity_class, len(id_list)) + + self._reg_cache_in_context2(sql, id_list, table_name, "int", entity_class, SuidType.DELETE) + + super().logsql("delete by id SQL:", sql) super().log_params(id_list) return self.beeSql.modify(sql, id_list) except Exception as e: @@ -222,13 +310,17 @@ class SuidRich(Suid): super().doBeforeReturnSimple() def select_fun(self, entity, functionType, field_for_fun): - if entity is None: + if not entity: return None try: super().doBeforePasreEntity(entity, SuidType.SELECT) - sql, params = self.objToSQL.toSelectFunSQL(entity, functionType, field_for_fun) - Logger.logsql("select fun SQL:", sql) + sql, params, table_name = self.objToSQL.toSelectFunSQL(entity, functionType, field_for_fun) + + entityClass = self._to_class_t(entity) + self._reg_cache_in_context(sql, params, table_name, "", entityClass) + + super().logsql("select fun SQL:", sql) super().log_params(params) r = self.beeSql.select_fun(sql, params) if r is None and functionType == FunctionType.COUNT: @@ -260,17 +352,21 @@ class SuidRich(Suid): # * @return the numbers of update record(s) successfully,if fails, return integer less than 0. # * @since 1.6.0 # */ - def updateBy(self, entity, condition: Condition=None, *whereFields): - if entity is None: + def updateBy(self, entity, condition: Condition = None, *whereFields): + if not entity: return None - - if whereFields is None or len(whereFields) == 0 or (len(whereFields) == 1 and (not whereFields[0] or whereFields[0].isspace())): - raise ParamBeeException("whereFields at least include one field.") + # the op method in condition can as whereFields + # if not whereFields or len(whereFields) == 0 or (len(whereFields) == 1 and (not whereFields[0] or whereFields[0].isspace())): + # raise ParamBeeException("whereFields at least include one field.") try: super().doBeforePasreEntity(entity, SuidType.UPDATE) - sql, params = self.objToSQL.toUpdateBySQL2(entity, condition, whereFields) - Logger.logsql("updateBy SQL:", sql) + sql, params, table_name = self.objToSQL.toUpdateBySQL2(entity, condition, whereFields) + + entityClass = self._to_class_t(entity) + self._reg_cache_in_context2(sql, params, table_name, "int", entityClass, SuidType.UPDATE) + + super().logsql("updateBy SQL:", sql) super().log_params(params) return self.beeSql.modify(sql, params) except Exception as e: @@ -283,22 +379,22 @@ class SuidRich(Suid): # *
If the field of entity is not null or empty, it will be translate to field=value in where part.Other can define with condition. # def update3(self, entity, condition: Condition=None, *updateFields): - def create_table(self, entityClass, is_drop_exist_table=None): + def create_table(self, entityClass, is_drop_exist_table = None): if is_drop_exist_table: sql0 = self.objToSQL.toDropTableSQL(entityClass) - Logger.logsql("drop table SQL:", sql0) + super().logsql("drop table SQL:", sql0) self.beeSql.modify(sql0) sql = self.objToSQL.toCreateSQL(entityClass) - Logger.logsql("create table SQL:", sql) + super().logsql("create table SQL:", sql) return self.beeSql.modify(sql) - def index_normal(self, entity_class, fields, index_name=None): + def index_normal(self, entity_class, fields, index_name = None): prefix = "idx_" index_type_tip = "normal" index_type = "" # normal will be empty self._index(entity_class, fields, index_name, prefix, index_type_tip, index_type) - def unique(self, entity_class, fields, index_name=None): + def unique(self, entity_class, fields, index_name = None): prefix = "uie_" index_type_tip = "unique" index_type = "UNIQUE " @@ -309,31 +405,35 @@ class SuidRich(Suid): self._index_modify(index_sql) def _index_modify(self, index_sql): - Logger.logsql("create index SQL:", index_sql) + super().logsql("create index SQL:", index_sql) self.beeSql.modify(index_sql) - def drop_index(self, entity_class, index_name=None): + def drop_index(self, entity_class, index_name = None): sql = self.objToSQL.to_drop_index_sql(entity_class, index_name) - Logger.logsql("drop index SQL:", sql) + super().logsql("drop index SQL:", sql) self.beeSql.modify(sql) # for custom SQL +# custom SQL do not support cache class PreparedSql(AbstractCommOperate): """ eg: """ - def select(self, sql, return_type_class, params=None, start=None, size=None): - if sql is None: - return None - if return_type_class is None: + def select(self, sql, return_type_class, params = None, start = None, size = None): + if not sql: return None + if not return_type_class: + return None + try: + sql = self.__adjust_placeholder(sql) + sql = SqlUtil.add_paging(sql, start, size) - Logger.logsql("select SQL(PreparedSql):", sql) + super().logsql("select SQL(PreparedSql):", sql) super().log_params(params) return self.beeSql.select(sql, return_type_class, params) except Exception as e: @@ -345,10 +445,11 @@ class PreparedSql(AbstractCommOperate): entity_list =preparedSql.select_dict("SELECT * FROM orders WHERE name=#{name} and id=#{id} and name=#{name}", Orders, params_dict ={"name":"bee1","id":4}) """ - def select_dict(self, sql, return_type_class, params_dict=None, start=None, size=None): + def select_dict(self, sql, return_type_class, params_dict = None, start = None, size = None): + params = None if params_dict: - sql, params_dict = SqlUtil.transform_sql(sql, params_dict) - return self.select(sql, return_type_class, params_dict, start, size) + sql, params = SqlUtil.transform_sql(sql, params_dict) + return self.select(sql, return_type_class, params, start, size) """ eg: @@ -358,13 +459,20 @@ class PreparedSql(AbstractCommOperate): """ # def modify(self, sql: str, params=None) -> int: - def modify(self, sql, params=None): + def modify(self, sql, params = None): try: - Logger.logsql("modify SQL(PreparedSql):", sql) + sql = self.__adjust_placeholder(sql) + super().logsql("modify SQL(PreparedSql):", sql) super().log_params(params) return self.beeSql.modify(sql, params) except Exception as e: raise BeeException(e) + + def __adjust_placeholder(self, sql): + placeholder = HoneyContext.get_placeholder() # in python different db have diffent placeholder + sql = sql.replace("%s", placeholder) + sql = sql.replace("?", placeholder) + return sql """ eg: @@ -373,10 +481,11 @@ class PreparedSql(AbstractCommOperate): updateNum = preparedSql.modify_dict(sql, params_dict) """ - def modify_dict(self, sql, params_dict=None): - if params_dict: - sql, params_dict = SqlUtil.transform_sql(sql, params_dict) - return self.modify(sql, params_dict) + def modify_dict(self, sql, params_dict = None): + params = None + if params_dict: + sql, params = SqlUtil.transform_sql(sql, params_dict) + return self.modify(sql, params) def __init__(self): super().__init__() diff --git a/src/bee/condition.py b/src/bee/condition.py index 0ca924b..687e362 100644 --- a/src/bee/condition.py +++ b/src/bee/condition.py @@ -1,23 +1,25 @@ -from abc import ABC, abstractmethod -from typing import List, Any +from abc import ABC, abstractmethod +from typing import List, Set, Any from bee.context import HoneyContext from bee.exception import ParamBeeException, BeeErrorGrammarException +from bee.name import NameCheckUtil from bee.name.naming_handler import NamingHandler -from bee.osql.enum import FunctionType, Op, OrderType, SuidType +from bee.osql.bee_enum import FunctionType, Op, OrderType, SuidType from bee.osql.logger import Logger from bee.osql.sqlkeyword import K -#since 1.6.0 + +# since 1.6.0 class Expression: - def __init__(self, field_name: str=None, Op: Op=None, op_type=None, value: Any=None, - op_num: int=None, value2: Any=None, value3: Any=None, value4: Any=None): + def __init__(self, field_name: str = None, Op: Op = None, op_type = None, value: Any = None, + op_num: int = None, value2: Any = None, value3: Any = None, value4: Any = None): self.field_name = field_name self.op_type = op_type if op_type else Op.get_name() if Op else None self.op = Op self.value = value - self.op_num = op_num # if op_num is not None else 2 # Default for binary operations + self.op_num = op_num # type num self.value2 = value2 self.value3 = value3 self.value4 = value4 @@ -41,11 +43,12 @@ class PreparedValue: class ConditionStruct: - def __init__(self, where: str, pv: List[PreparedValue], values: List, suidType:SuidType, selectFields:str, start:int, size:int, has_for_update:bool): + def __init__(self, where: str, pv: List[PreparedValue], values: List, suidType:SuidType, whereFields:Set, selectFields:str, start:int, size:int, has_for_update:bool): self.where = where self.pv = pv self.values = values self.suidType = suidType + self.whereFields = whereFields self.selectFields = selectFields self.start = start self.size = size @@ -53,15 +56,16 @@ class ConditionStruct: def __repr__(self): return str(self.__dict__) + class ConditionUpdateSetStruct: - def __init__(self, updateSet: str, pv: List[PreparedValue], values: List, suidType:SuidType): + def __init__(self, updateSet: str, pv: List[PreparedValue], values: List, suidType:SuidType, updateSetFields:Set): self.updateSet = updateSet self.pv = pv self.values = values self.suidType = suidType - + self.updateSetFields = updateSetFields def __repr__(self): return str(self.__dict__) @@ -126,11 +130,11 @@ class Condition(ABC): pass @abstractmethod - def forUpdate(self) -> 'Condition': + def start(self, start:int) -> 'Condition': pass @abstractmethod - def start(self, start:int) -> 'Condition': + def forUpdate(self) -> 'Condition': pass @abstractmethod @@ -145,7 +149,7 @@ class Condition(ABC): def getSuidType(self) -> 'Condition': pass - ### ###########-------just use in update-------------start- + # ## ###########-------just use in update-------------start- @abstractmethod def setAdd(self, field: str, value: Any) -> 'Condition': pass @@ -166,14 +170,16 @@ class Condition(ABC): def set(self, field: str, value: Any) -> 'Condition': pass + @abstractmethod + def setWithField(self, field1: str, field2: str) -> 'Condition': + pass + @abstractmethod def setNull(self, field: str) -> 'Condition': pass - @abstractmethod - def setWithField(self, field1: str, field2: str) -> 'Condition': - pass - ### ###########-------just use in update-------------end- + # ## ###########-------just use in update-------------end- + class ConditionImpl(Condition): @@ -184,57 +190,57 @@ class ConditionImpl(Condition): self.update_set_exp = [] self.update_set_fields = set() - self.__isStartOrderBy = True # 实例变量 + self.__isStartOrderBy = True self.__isStartGroupBy = True self.__isStartHaving = True self.__suidType = SuidType.SELECT - def __check_field(self, field): - pass # TODO + def __check_one_field(self, field): + NameCheckUtil._check_one_name(field) def op(self, field: str, Op: Op, value: Any) -> 'ConditionImpl': - self.__check_field(field) - exp = Expression(field_name=field, Op=Op, value=value, op_num=2) + self.__check_one_field(field) + exp = Expression(field_name = field, Op = Op, value = value, op_num = 2) self.expressions.append(exp) self.where_fields.add(field) return self def and_(self) -> 'ConditionImpl': - exp = Expression(op_type=K.and_(), op_num=1) + exp = Expression(op_type = K.and_(), op_num = 1) self.expressions.append(exp) return self def or_(self) -> 'ConditionImpl': - exp = Expression(op_type=K.or_(), op_num=1) + exp = Expression(op_type = K.or_(), op_num = 1) self.expressions.append(exp) return self def not_(self) -> 'ConditionImpl': - exp = Expression(op=Op.eq, op_type=K.not_(), op_num=1) + exp = Expression(op = Op.eq, op_type = K.not_(), op_num = 1) self.expressions.append(exp) return self def l_parentheses(self) -> 'ConditionImpl': - exp = Expression(value="(", op_num=-2) + exp = Expression(value = "(", op_num = -2) self.expressions.append(exp) return self def r_parentheses(self) -> 'ConditionImpl': - exp = Expression(value=")", op_num=-1) + exp = Expression(value = ")", op_num = -1) self.expressions.append(exp) return self def between(self, field: str, low: Any, high: Any) -> 'ConditionImpl': - self.__check_field(field) - exp = Expression(field_name=field, op_type=K.between(), value=low, value2=high, op_num=3) + self.__check_one_field(field) + exp = Expression(field_name = field, op_type = K.between(), value = low, value2 = high, op_num = 3) self.expressions.append(exp) self.where_fields.add(field) return self def opWithField(self, field: str, op: Op, field2: str) -> 'ConditionImpl': - self.__check_field(field) - self.__check_field(field2) - expr = Expression(field_name=field, Op=op, value=field2, op_num=-3) + self.__check_one_field(field) + self.__check_one_field(field2) + expr = Expression(field_name = field, Op = op, value = field2, op_num = -3) self.expressions.append(expr) self.where_fields.add(field) return self @@ -242,8 +248,8 @@ class ConditionImpl(Condition): # 'forUpdate', 'groupBy', 'orderBy', 'selectField', 'size', 'start' def groupBy(self, field:str) -> 'ConditionImpl': - self.__check_field(field) - exp = Expression(field_name=field, op_type=K.group_by(), op_num=-4) + self.__check_one_field(field) + exp = Expression(field_name = field, op_type = K.group_by(), op_num = -4) if self.__isStartGroupBy: self.__isStartGroupBy = False @@ -255,8 +261,8 @@ class ConditionImpl(Condition): # having(FunctionType.MIN, "field", Op.ge, 60)-->having min(field)>=60 def having(self, functionType:FunctionType, field: str, op: Op, value: Any) -> 'ConditionImpl': - self.__check_field(field) - exp = Expression(field_name=field, Op=op, value=value, op_num=5) + self.__check_one_field(field) + exp = Expression(field_name = field, Op = op, value = value, op_num = 5) exp.value2 = functionType if self.__isStartHaving: @@ -274,8 +280,8 @@ class ConditionImpl(Condition): __COMMA = "," def orderBy(self, field:str) -> 'ConditionImpl': - self.__check_field(field) - exp = Expression(field_name=field, op_type=K.order_by(), op_num=12) + self.__check_one_field(field) + exp = Expression(field_name = field, op_type = K.order_by(), op_num = 12) self.expressions.append(exp) if self.__isStartOrderBy: self.__isStartOrderBy = False @@ -285,8 +291,8 @@ class ConditionImpl(Condition): return self def orderBy2(self, field:str, orderType:OrderType) -> 'ConditionImpl': - self.__check_field(field) - exp = Expression(field_name=field, op_type=K.order_by(), op_num=13) + self.__check_one_field(field) + exp = Expression(field_name = field, op_type = K.order_by(), op_num = 13) exp.value2 = orderType.get_name() self.expressions.append(exp) if self.__isStartOrderBy: @@ -297,8 +303,8 @@ class ConditionImpl(Condition): return self def orderBy3(self, functionType:FunctionType, field:str, orderType:OrderType) -> 'ConditionImpl': - self.__check_field(field) - exp = Expression(field_name=field, op_type=K.order_by(), op_num=14) + self.__check_one_field(field) + exp = Expression(field_name = field, op_type = K.order_by(), op_num = 14) exp.value2 = orderType.get_name() exp.value3 = functionType.get_name() self.expressions.append(exp) @@ -310,15 +316,22 @@ class ConditionImpl(Condition): return self def selectField(self, *fields:str) -> 'ConditionImpl': - self.__check_field(fields) - exp = Expression(value=fields, op_num=20) + if fields: + if len(fields) == 1: + if fields[0]: + NameCheckUtil.check_fields(fields[0].replace(" ","")) + else: + for field in fields: + self.__check_one_field(field) + exp = Expression(value = fields, op_num = 20) self.expressions.append(exp) return self def start(self, start:int) -> 'ConditionImpl': - if start is None or start < 0: # if not 0: is True + #  if not 0: is True + if start is None or start == '' or start < 0: raise ParamBeeException("Parameter 'start' need >=0 .") - exp = Expression(value=start, op_num=21) + exp = Expression(value = start, op_num = 21) self.expressions.append(exp) return self @@ -326,7 +339,12 @@ class ConditionImpl(Condition): if not size or size <= 0: raise ParamBeeException("Parameter 'size' need >0 .") - exp = Expression(value=size, op_num=22) + exp = Expression(value = size, op_num = 22) + self.expressions.append(exp) + return self + + def forUpdate(self) -> 'ConditionImpl': + exp = Expression(op_type = K.for_update(), op_num = 30) self.expressions.append(exp) return self @@ -338,42 +356,73 @@ class ConditionImpl(Condition): def getSuidType(self) -> 'SuidType': return self.__suidType - def forUpdate(self) -> 'ConditionImpl': - exp = Expression(op_type=K.for_update(), op_num=30) - self.expressions.append(exp) - return self - - ### ###########-------just use in update-------------start- + # ## ###########-------just use in update set-------------start- + # salary = salary + 1000 def setAdd(self, field: str, value: Any) -> 'ConditionImpl': - pass + self.__check_one_field(field) + exp = Expression(field_name = field, value = value, op_num = 52, op_type = "+") + self.update_set_exp.append(exp) + self.update_set_fields.add(field) + return self + # salary = salary * 1.1 def setMultiply(self, field: str, value: Any) -> 'ConditionImpl': - pass - - def setAdd2(self, field: str, otherFieldName: str) -> 'ConditionImpl': - pass + self.__check_one_field(field) + exp = Expression(field_name = field, value = value, op_num = 53, op_type = "*") + self.update_set_exp.append(exp) + self.update_set_fields.add(field) + return self - def setMultiply2(self, field: str, otherFieldName: str) -> 'ConditionImpl': - pass + def setAdd2(self, field1: str, field2: str) -> 'ConditionImpl': + self.__check_one_field(field1) + self.__check_one_field(field2) + exp = Expression(field_name = field1, value = field2, op_num = 54, op_type = "+") + self.update_set_exp.append(exp) + self.update_set_fields.add(field1) + self.update_set_fields.add(field2) + return self + + def setMultiply2(self, field1: str, field2: str) -> 'ConditionImpl': + self.__check_one_field(field1) + self.__check_one_field(field2) + exp = Expression(field_name = field1, value = field2, op_num = 55, op_type = "*") + self.update_set_exp.append(exp) + self.update_set_fields.add(field1) + self.update_set_fields.add(field2) + return self def set(self, field: str, value: Any) -> 'ConditionImpl': - pass - - def setNull(self, field: str) -> 'ConditionImpl': - pass - - def setWithField(self, field1: str, field2: str) -> 'Condition': - pass - ### ###########-------just use in update-------------end- + self.__check_one_field(field) + exp = Expression(field_name = field, value = value, op_num = 60) + self.update_set_exp.append(exp) + self.update_set_fields.add(field) + return self + # setWithField(field1,field2)--> set field1 = field2 + def setWithField(self, field1: str, field2: str) -> 'ConditionImpl': + self.__check_one_field(field1) + self.__check_one_field(field2) + exp = Expression(field_name = field1, value = field2, op_num = 61) + self.update_set_exp.append(exp) + self.update_set_fields.add(field1) + self.update_set_fields.add(field2) + return self + def setNull(self, field: str) -> 'ConditionImpl': + self.__check_one_field(field) + exp = Expression(field_name = field, value = None, op_num = 62) + self.update_set_exp.append(exp) + self.update_set_fields.add(field) + return self + + # ## ###########-------just use in update set-------------end- # parse where def parseCondition(self) -> ConditionStruct: return ParseCondition.parse(self.expressions, self) # parse update set - def parseConditionUpdateSet(self) -> ConditionStruct: + def parseConditionUpdateSet(self) -> ConditionUpdateSetStruct: return ParseCondition.parseUpdateSet(self.update_set_exp, self) @@ -385,8 +434,96 @@ class ParseCondition: # parse update set @staticmethod - def parseUpdateSet(update_set_exp, condition:Condition) -> ConditionUpdateSetStruct: - pass + def parseUpdateSet(expressions, condition:Condition) -> ConditionUpdateSetStruct: + update_set_sql = [] + prepared_values = [] + values = [] + COMMA = "," + + is_first = True + suidType = condition.getSuidType() + ph = ParseCondition.__getPlaceholder() + + for exp in expressions: + + if is_first: + is_first = False + else: + update_set_sql.append(COMMA) + + column_name = NamingHandler.toColumnName(exp.field_name) + + # salary = salary * 1.1 + # salary = salary + 1000 + if exp.op_num == 52 or exp.op_num == 53: + clause = f"{column_name} = {column_name} {exp.op_type} {ph}" + update_set_sql.append(clause) + prepared_values.append(PreparedValue(type(exp.value), exp.value)) + values.append(exp.value) + + # SET salary = salary + bonus; + elif exp.op_num == 54 or exp.op_num == 55: + clause = f"{column_name} = {column_name} {exp.op_type} {exp.value}" + update_set_sql.append(clause) + + # manager_id = 1 + elif exp.op_num == 60: + clause = f"{column_name} = {ph}" + update_set_sql.append(clause) + prepared_values.append(PreparedValue(type(exp.value), exp.value)) + values.append(exp.value) + + # salary = bonus #61 + # remark = null #62 + elif exp.op_num == 61 or exp.op_num == 62: + clause = f"{column_name} = {exp.value}" + update_set_sql.append(clause) + + elif exp.op_num == -2: # Left parenthesis + update_set_sql.append("(") + elif exp.op_num == -1: # Right parenthesis + update_set_sql.append(")") + + else: + Logger.warn(f"Unknown operation number: {exp.op_num}") + +# UPDATE employees +# SET salary = 60000, department = 'HR' +# WHERE employee_id = 101; + +# UPDATE employees +# SET salary = CASE +# WHEN department = 'Sales' THEN salary * 1.2 +# WHEN department = 'HR' THEN salary * 1.1 +# ELSE salary +# END; + +# UPDATE employees e +# JOIN departments d ON e.department_id = d.department_id +# SET e.salary = e.salary * 1.1 +# WHERE d.department_name = 'Engineering'; + +# UPDATE employees +# SET salary = ( +# SELECT AVG(salary) +# FROM employees +# WHERE department = 'HR' +# ) +# WHERE employee_id = 102; + +# 6. 使用ORDER BY和LIMIT:限制更新数量 +# 按特定顺序更新前N行(适用于MySQL等支持的数据库)。 +# +# sql +# UPDATE scores +# SET score = 100 +# ORDER BY submission_time DESC +# LIMIT 5; + + # Join all where clauses into a single string + updateSet_str = "".join(update_set_sql) + + return ConditionUpdateSetStruct(updateSet_str, prepared_values, values, suidType, condition.update_set_fields) # parse where @staticmethod @@ -415,7 +552,7 @@ class ParseCondition: for exp in expressions: # exp.field_name ->column_name column_name = NamingHandler.toColumnName(exp.field_name) - if exp.op_num == 2: # Binary operation + if exp.op_num == 2: # Binary operation # op("name", Op.ne, "bee1") is_need_and = adjust_and() if exp.value is None: where_clause = f"{column_name} {K.isnull()}" @@ -425,6 +562,25 @@ class ParseCondition: values.append(exp.value) where_clauses.append(where_clause) is_need_and = True + + elif exp.op_num == -3: # eg:field1=field2 + is_need_and = adjust_and() + where_clause = f"{column_name} {exp.op} {exp.value}" + where_clauses.append(where_clause) + is_need_and = True + + elif exp.op_num == 1: # Logical operator (AND, OR, NOT) + if exp.op_type == K.not_(): + is_need_and = adjust_and() + where_clauses.append(f" {exp.op_type} ") + is_need_and = False + elif exp.op_num == -2: # Left parenthesis + is_need_and = adjust_and() + where_clauses.append("(") + elif exp.op_num == -1: # Right parenthesis + where_clauses.append(")") + is_need_and = True + elif exp.op_num == 3: # BETWEEN is_need_and = adjust_and() where_clause = f"{column_name} {exp.op_type} {ph} {K.and_()} {ph}" @@ -434,11 +590,6 @@ class ParseCondition: values.append(exp.value) values.append(exp.value2) is_need_and = True - elif exp.op_num == -3: # eg:field1=field2 - is_need_and = adjust_and() - where_clause = f"{column_name} {exp.op} {exp.value}" - where_clauses.append(where_clause) - is_need_and = True elif exp.op_num == -4: # group by if suidType != SuidType.SELECT: raise BeeErrorGrammarException(suidType.get_name() + " do not support 'group by' !") @@ -469,30 +620,18 @@ class ParseCondition: where_clauses.append(column_name) if 13 == exp.op_num or 14 == exp.op_num: # 指定 desc,asc - where_clauses.append(" "); + where_clauses.append(" ") where_clauses.append(exp.value2) - elif exp.op_num == 1: # Logical operator (AND, OR, NOT) - if exp.op_type == K.not_(): - is_need_and = adjust_and() - where_clauses.append(f" {exp.op_type} ") - is_need_and = False - elif exp.op_num == -2: # Left parenthesis - is_need_and = adjust_and() - where_clauses.append("(") - elif exp.op_num == -1: # Right parenthesis - where_clauses.append(")") - is_need_and = True - - elif exp.op_num == 20: + elif exp.op_num == 20: # selectField("name,age") __selectFields = exp.value - elif exp.op_num == 21: + elif exp.op_num == 21: # start __start = exp.value - elif exp.op_num == 22: + elif exp.op_num == 22: # size __size = exp.value - elif exp.op_num == 30: # for update TODO + elif exp.op_num == 30: # for update __has_for_update = True else: Logger.warn(f"Unknown operation number: {exp.op_num}") @@ -500,5 +639,5 @@ class ParseCondition: # Join all where clauses into a single string where_clause_str = "".join(where_clauses) - return ConditionStruct(where_clause_str, prepared_values, values, suidType, __selectFields, __start, __size, __has_for_update) + return ConditionStruct(where_clause_str, prepared_values, values, suidType, condition.where_fields, __selectFields, __start, __size, __has_for_update) diff --git a/src/bee/config.py b/src/bee/config.py index 4dcb66d..209691d 100644 --- a/src/bee/config.py +++ b/src/bee/config.py @@ -2,50 +2,101 @@ import json import os from bee.exception import ConfigBeeException -from bee.osql.const import SysConst, DatabaseConst +from bee.osql.const import DatabaseConst from bee.osql.logger import Logger class PreConfig: # suggest set project root path for it - config_folder_root_path = None #replace with config_path since 1.6.0 + config_folder_root_path = None # replace with config_path since 1.6.0 config_path = "" - # value is:lower,upper - sql_key_word_case = None + config_properties_file_name = "bee.properties" + config_json_file_name = "bee.json" - sql_placeholder="?" + # # value is:lower,upper + # sql_key_word_case = None + # + # sql_placeholder = "?" +# class SingletonMeta(type): +# _instances = {} +# +# def __call__(self, *args, **kwargs): +# print("------SingletonMeta---__call__--------") +# if self not in self._instances: +# self._instances[self] = super().__call__(*args, **kwargs) +# return self._instances[self] +# +# class HoneyConfig(metaclass=SingletonMeta): class HoneyConfig: - dbName =None - host =None - user =None - password=None - database=None - port =None + dbname = None + host = None + user = None + password = None + database = None + port:int = None + + # value is:lower,upper + sql_key_word_case = "lower" + sql_placeholder = "?" + + show_sql:bool = False + show_sql_params:bool = False + show_sql_spent_time:bool = False + show_sql_spent_time_min_ms:int = 0 - _loaded = False # 标记是否已加载配置 + # (DB<-->Python), + # 1: order_no<-->orderNo + # 2: ORDER_NO<-->orderNo + # 3: original, + # 4: ORDER_NO<-->order_no (DbUpperAndPythonLower) + naming_translate_type:int = 3 + naming_to_lower_before:bool = True - __db_config_data=None + # cache的要提前用,不能设置为None. 是可以的,之前是因为Cache在属性使用了__cacheArrayIndex = CacheArrayIndex()引起;import时就会运行到。 + cache_max_size:int = 1000 + cache_start_delete_rate:float = 0.6 + cache_full_used_rate:float = 0.9 + cache_full_clear_rate:float = 0.2 + cache_timeout:int = 10000 + cache_key_use_md5:bool = True + + # >= this-value will do not put in cache + cache_donot_put_cache_result_min_size:int = 100 + + _loaded = False # 标记是否已加载配置 + __db_config_data = None __instance = None - def __new__(cls): + # 将 PreConfig 作为参数,传入。 + def __new__(cls): if cls.__instance is None: - Logger.debug("HoneyConfig.__new__") + # Logger.debug("HoneyConfig.__new__") + Logger.debug("HoneyConfig instance...") cls.__instance = super().__new__(cls) cls.__loadConfigInProperties(cls) cls.__loadConfigInJson(cls) if cls.port: - cls.port=int(cls.port) - - if cls.__db_config_data is None: - Logger.info("Default loading and init configuration file failed!") + cls.port = int(cls.port) + if cls.__db_config_data is None: + Logger.info("Default loading and init configuration file failed!") return cls.__instance + # def __init__(self): + # print("--------HoneyConfig-----__init__-------") + # # + # # def __call__(self): + # # print("--------HoneyConfig-----__call__-------") + # + # def __getattribute__(self, item): + # print(f"Accessing attribute: {item}") + # return super().__getattribute__(item) + @staticmethod def __adjust_config_file(cls, config_file): @@ -73,16 +124,18 @@ class HoneyConfig: def __loadConfigInProperties(cls): if cls._loaded: return - config_file = SysConst.configPropertiesFileName # 文件路径 + config_file = PreConfig.config_properties_file_name # 文件路径 + old_config_file=config_file try: config_file = cls.__adjust_config_file(cls, config_file) if not os.path.isfile(config_file): - Logger.info("Not found the file bee.properties!") + Logger.info(f"Not found the file {old_config_file}!") return with open(config_file, 'r') as file: cls._loaded = True # 设置为已加载 Logger.info("Loading config file: " + config_file) + annotations = cls.__annotations__ for line in file: line = line.strip() # 跳过空行和注释 @@ -104,34 +157,88 @@ class HoneyConfig: # 将值赋给对应的属性 if hasattr(cls, attr_name): setattr(cls, attr_name, value) + + # 检查键是否以 'bee.' 开头 + elif key.startswith('bee.'): + # 获取属性名称 + attr_name = key[len('bee.'):] + # 将值赋给对应的属性 + if hasattr(cls, attr_name): + # setattr(cls, attr_name, value) # 数据是否要转换类型? 要,在以下转换 + + # 获取类型提示(Python 3.5+) + type_hint = annotations.get(attr_name) + init_value = getattr(cls, attr_name) + + # print(" value:",init_value, "attr_name:",attr_name) + + if type_hint is not None: # 优先使用类型注解 + target_type = type_hint + # print("target_type: ",target_type," value:",init_value) + elif init_value is not None: # 其次使用默认值的类型 + target_type = type(init_value) + else: # 既无注解也无默认值,保持原样 + target_type = None + + # print("target_type: ",target_type," value:",init_value, "attr_name:",attr_name) + + # 在 Python 中,将字符串 'False' 转换为布尔型变量时,直接使用 bool() 函数会得到 True,因为非空字符串在 Python 中会被视为 True + + try: + converted_value = None + # converted_value = value if target_type is None else target_type(value) + # print("converted_value: ",converted_value) + if target_type is None: + converted_value = value + elif type_hint is not None and type_hint is bool: + converted_value = value.lower() == 'true' + elif type_hint is not None: + converted_value = target_type(value) + elif target_type is bool: + converted_value = value.lower() == 'true' + else: + converted_value = target_type(init_value) + + # print("target_type: ",target_type," converted_value:",converted_value, "attr_name:",attr_name) + setattr(cls, attr_name, converted_value) + except (ValueError, TypeError) as e: + raise ValueError(f"Can not transform {value} to {target_type} (attr_name: {attr_name})") from e cls.__db_config_data = cls.__instance.get_db_config_dict() except OSError as err: Logger.info(err) # raise ConfigBeeException(err) + + bool_map = {'True': True, 'False': False} @staticmethod def __loadConfigInJson(cls): if cls._loaded: return - config_file = SysConst.configJsonFileName + config_file = PreConfig.config_json_file_name + old_config_file=config_file try: config_file = cls.__adjust_config_file(cls, config_file) - Logger.info("Loading config file: "+config_file) + + if not os.path.isfile(config_file): + Logger.warn(f"Not found the file {old_config_file}!") + return + + Logger.info("Loading config file: " + config_file) with open(config_file, 'r') as file: cls._loaded = True # 设置为已加载 cls.__db_config_data = json.load(file) - cls.dbName = cls.__db_config_data.get("dbName") + cls.dbname = cls.__db_config_data.get("dbname") except OSError as err: - Logger.error(err) + Logger.info(err) def __adjust_db_path_for_sqllite(self): cls = type(self) - t_dbName = cls.__db_config_data['dbName'] + t_dbName = cls.__db_config_data['dbname'] if t_dbName.lower() == DatabaseConst.SQLite.lower(): t_database = cls.__db_config_data['database'] @@ -147,49 +254,54 @@ class HoneyConfig: raise ConfigBeeException(f"File not found in current path or adjust path: {newPath}") cls.__db_config_data['database'] = newPath - def get_db_config_dict(self): + def get_db_config_dict(self): """将DB相关的类属性打包成字典并返回""" - cls=type(self) + cls = type(self) if cls.__db_config_data: - #adjust db path + # adjust db path self.__adjust_db_path_for_sqllite() return cls.__db_config_data - cls.__db_config_data={} + cls.__db_config_data = {} - if HoneyConfig.dbName: - cls.__db_config_data['dbName'] = HoneyConfig.dbName - if HoneyConfig.host: + if HoneyConfig.dbname: + cls.__db_config_data['dbname'] = HoneyConfig.dbname + if HoneyConfig.host: cls.__db_config_data['host'] = HoneyConfig.host - if HoneyConfig.user: + if HoneyConfig.user: cls.__db_config_data['user'] = HoneyConfig.user - if HoneyConfig.password: + if HoneyConfig.password: cls.__db_config_data['password'] = HoneyConfig.password - if HoneyConfig.database: - cls.__db_config_data['database'] = HoneyConfig.database #adjust db path - if HoneyConfig.port: + if HoneyConfig.database: + cls.__db_config_data['database'] = HoneyConfig.database # adjust db path + if HoneyConfig.port: cls.__db_config_data['port'] = int(HoneyConfig.port) self.__adjust_db_path_for_sqllite() return cls.__db_config_data - def set_db_config_dict(self,config): - cls=type(self) - cls.__db_config_data=config + def set_db_config_dict(self, config): + if not config: + return + Logger.info("Reset db_config_data") + cls = type(self) + if cls.__db_config_data: + cls.__db_config_data = {} + cls.__db_config_data = config - if config: - Logger.info("Reset db_config_data") - if config.get("dbName"): - if cls.__db_config_data: - cls.__db_config_data={} - cls.__db_config_data["dbName"] = config.get("dbName") + if config.get("dbname"): + cls.__db_config_data["dbname"] = config.get("dbname") + HoneyConfig.dbName=config.get("dbname") - def get_dbName(self): - return HoneyConfig.dbName.lower() + def get_dbname(self): + if HoneyConfig.dbname is None: + return None + + return HoneyConfig.dbname.lower() - def set_dbName(self, dbName): - HoneyConfig.dbName = dbName - + def set_dbname(self, dbname): + Logger.info("set database name:" + dbname) + HoneyConfig.dbname = dbname # if __name__ == '__main__': # print("start") diff --git a/src/bee/conn_builder.py b/src/bee/conn_builder.py index da84b6c..47983f9 100644 --- a/src/bee/conn_builder.py +++ b/src/bee/conn_builder.py @@ -8,58 +8,59 @@ from bee.osql.logger import Logger class ConnectionBuilder: - _already_print=False + _already_print = False @staticmethod def build_connect(config): - dbName = None - if SysConst.dbName in config: + dbname = None + if SysConst.dbname in config: tempConfig = config.copy() - dbName = tempConfig.pop(SysConst.dbName, None) + dbname = tempConfig.pop(SysConst.dbname, None) else: tempConfig = config # Map database names to their respective module names and connection functions db_modules = { DatabaseConst.MYSQL.lower(): 'pymysql', - DatabaseConst.SQLite.lower(): 'sqlite3', - DatabaseConst.ORACLE.lower(): 'cx_Oracle', - DatabaseConst.PostgreSQL.lower(): 'psycopg2', + DatabaseConst.SQLite.lower(): 'sqlite3', + DatabaseConst.ORACLE.lower(): 'cx_Oracle', + DatabaseConst.PostgreSQL.lower(): 'psycopg2', } - # Check if the dbName is supported + # Check if the dbname is supported # if dbName not in db_modules: - if dbName is None: - # raise ValueError("Need set the dbName in Config!") - Logger.info("Need set the dbName in Config!") + if dbname is None: + # raise ValueError("Need set the dbname in Config!") + Logger.info("Need set the dbname in Config!") return None - dbName = dbName.lower() + original_dbname = dbname + dbname = dbname.lower() - if SysConst.dbModuleName in config: #优先使用dbModuleName,让用户可以有选择覆盖默认配置的机会 + if SysConst.dbModuleName in config: # 优先使用dbModuleName,让用户可以有选择覆盖默认配置的机会 dbModuleName = tempConfig.pop(SysConst.dbModuleName, None) - elif dbName not in db_modules: - # raise ValueError(f"Database type '{dbName}' is not supported, need config dbModuleName.") - Logger.info(f"Database type '{dbName}' is not supported, need config dbModuleName.") # TODO + elif dbname not in db_modules: + # raise ValueError(f"Database type '{dbname}' is not supported, need config dbModuleName.") + Logger.warn(f"Database type '{dbname}' is not supported, need config dbModuleName.") # todo return None else: - dbModuleName = db_modules[dbName] - + dbModuleName = db_modules[dbname] db_module = importlib.import_module(dbModuleName) if not ConnectionBuilder._already_print: - Logger.info(f"Database driver use: {dbModuleName}!") - ConnectionBuilder._already_print=True + Logger.info(f"Database driver use {dbModuleName} for {original_dbname}") + ConnectionBuilder._already_print = True # Now create the connection using the imported module - if dbName == DatabaseConst.MYSQL.lower(): + if dbname == DatabaseConst.MYSQL.lower(): return db_module.connect(**tempConfig) - elif dbName == DatabaseConst.SQLite.lower(): + elif dbname == DatabaseConst.SQLite.lower(): return db_module.connect(**tempConfig) - - - + elif dbname == DatabaseConst.ORACLE.lower(): + return db_module.connect('scott/Bee123456@192.168.1.5/orcl') + # return db_module.connect('scott/Bee123456@localhost:1521/orcl') + # return db_module.connect('scott/Bee123456@localhost:1521:orcl') # ### 2. 使用 `psycopg2`(PostgreSQL) # @@ -73,7 +74,6 @@ class ConnectionBuilder: # database='your_database' # ) - # import cx_Oracle # # connection = cx_Oracle.connect('username/password@localhost/orcl') diff --git a/src/bee/context.py b/src/bee/context.py index 78c5fd8..c1601ba 100644 --- a/src/bee/context.py +++ b/src/bee/context.py @@ -1,14 +1,18 @@ # import sqlite3 # import pymysql -from bee.config import HoneyConfig, PreConfig +import threading + +from bee.config import HoneyConfig from bee.conn_builder import ConnectionBuilder from bee.factory import BeeFactory from bee.osql.const import DatabaseConst, SysConst +from bee.osql.bee_enum import LocalType + class HoneyContext: - dbName=None + dbname = None @staticmethod def get_connection(): @@ -19,9 +23,7 @@ class HoneyContext: if HoneyContext.is_active_conn(conn): return conn - honeyConfig = HoneyConfig() - config = honeyConfig.get_db_config_dict() - + config = HoneyConfig().get_db_config_dict() HoneyContext.__setDbName(config) conn = ConnectionBuilder.build_connect(config) factory.set_connection(conn) @@ -29,18 +31,17 @@ class HoneyContext: @staticmethod def __setDbName(config): - if SysConst.dbName in config: - dbName = config.get(SysConst.dbName, None) - if dbName: - HoneyContext.dbName = dbName + if SysConst.dbname in config: + dbname = config.get(SysConst.dbname, None) + if dbname: + HoneyContext.dbname = dbname @staticmethod def get_placeholder(): - honeyConfig=HoneyConfig() - dbName=honeyConfig.get_dbName() + dbName = HoneyConfig().get_dbname() - if dbName is None: + if not dbName: return None elif dbName == DatabaseConst.MYSQL.lower() or dbName == DatabaseConst.PostgreSQL.lower(): return "%s" @@ -50,20 +51,18 @@ class HoneyContext: # query = "SELECT * FROM users WHERE username = :username AND age = :age" return ":" else: - return PreConfig.sql_placeholder - - + return HoneyConfig.sql_placeholder + @staticmethod def is_active_conn(conn): - honeyConfig=HoneyConfig() - dbName=honeyConfig.get_dbName().lower() + dbName = HoneyConfig().get_dbname().lower() if dbName is None: return False elif dbName == DatabaseConst.MYSQL.lower(): try: - conn.ping(reconnect=True) + conn.ping(reconnect = True) return True except Exception: return False @@ -74,12 +73,12 @@ class HoneyContext: # return True # except Exception: # return False - elif dbName == DatabaseConst.ORACLE.lower(): - try: + elif dbName == DatabaseConst.ORACLE.lower(): + try: # For Oracle, we can use the `ping` method if using cx_Oracle conn.ping() return True - except Exception: + except Exception: return False # elif dbName == DatabaseConst.PostgreSQL.lower(): # try: @@ -88,7 +87,108 @@ class HoneyContext: # return True # except Exception: # return False - ## TODO: support other DB + # # todo: support other DB - return False + return False + + # 使用单个通用的 thread-local 存储 + __local_data = threading.local() + + @staticmethod + def _get_storage(): + """获取线程存储字典,如果不存在则初始化(静态方法)""" + if not hasattr(HoneyContext.__local_data, 'storage'): + HoneyContext.__local_data.storage = {} + return HoneyContext.__local_data.storage + + @staticmethod + def _set_data(local_type:LocalType, key = None, value = None): + """ + 设置线程局部数据(静态方法) + :param local_type: DataType 枚举值,指定要存储的数据类型 + :param key: 存储的key + :param value: 要存储的值 + """ + storage = HoneyContext._get_storage() + + if not value or not key or not key.strip(): + return + if local_type not in storage: + storage[local_type] = {} + + # print("----local------: "+key) + # print(str(value)) + storage[local_type][key] = value + + @staticmethod + def get_data(local_type:LocalType, key = None): + """ + 获取线程局部数据(静态方法) + :param local_type: DataType 枚举值 + :param key: 存储的key + :return: 存储的值或 None + """ + storage = HoneyContext._get_storage() + + if local_type not in storage or not key: + return None + return storage[local_type].get(key) + + @staticmethod + def _remove_data(local_type:LocalType, key = None): + """ + 移除线程局部数据(静态方法) + :param local_type: DataType 枚举值 + :param key: 存储的key + """ + storage = HoneyContext._get_storage() + + if local_type in storage and key: + storage[local_type].pop(key, None) + + @staticmethod + def _remove_one_local_type(local_type:LocalType): + storage = HoneyContext._get_storage() + if local_type in storage: + storage.pop(local_type, None) + + # __local = threading.local() + # @staticmethod + # def getCacheInfo(sql:str): + # if not sql or not hasattr(HoneyContext.__local, '_cache'): + # return None + # return HoneyContext.__local._cache.get(sql) + # + # @staticmethod + # def _setCacheInfo(sql:str, value:CacheSuidStruct): + # if not sql or not sql.strip(): + # return + # + # if not hasattr(HoneyContext.__local, '_cache'): + # HoneyContext.__local._cache = {} + # + # HoneyContext.__local._cache[sql] = value + # + # @staticmethod + # def _deleteCacheInfo(sql:str): + # pass + + @staticmethod + def isMySql(): + dbName = HoneyConfig().get_dbname() + return dbName == DatabaseConst.MYSQL.lower() + + @staticmethod + def isSQLite(): + dbName = HoneyConfig().get_dbname() + return dbName == DatabaseConst.SQLite.lower() + + @staticmethod + def isOracle(): + dbName = HoneyConfig().get_dbname() + return dbName == DatabaseConst.ORACLE.lower() + + @staticmethod + def get_dbname(): + return HoneyConfig().get_dbname() diff --git a/src/bee/custom.py b/src/bee/custom.py new file mode 100644 index 0000000..f6bc54e --- /dev/null +++ b/src/bee/custom.py @@ -0,0 +1,12 @@ + + +class Custom: + + @staticmethod + def custom_pk_statement(): + return "int(11)" + # raise NotImplementedError + + @staticmethod + def custom_to_page_sql(sql, start, size): + raise NotImplementedError diff --git a/src/bee/exception.py b/src/bee/exception.py index b5a6cdc..46381e2 100644 --- a/src/bee/exception.py +++ b/src/bee/exception.py @@ -46,8 +46,11 @@ class ConfigBeeException(BeeException): ... class SqlBeeException(BeeException): ... + class ParamBeeException(BeeException): ... + class BeeErrorNameException(BeeException): ... + class BeeErrorGrammarException(BeeException): ... diff --git a/src/bee/factory.py b/src/bee/factory.py index f2cc18e..0c9bc62 100644 --- a/src/bee/factory.py +++ b/src/bee/factory.py @@ -1,5 +1,6 @@ -# from bee.honeyfactory import HoneyFactory -from bee.name.naming import * +from bee.config import HoneyConfig +from bee.name.naming import NameTranslate, UnderScoreAndCamelName, \ + UpperUnderScoreAndCamelName, OriginalName, DbUpperAndPythonLower class BeeFactory: @@ -7,7 +8,7 @@ class BeeFactory: __connection = None __instance = None - __honeyFactory=None + __honeyFactory = None def __new__(cls): if cls.__instance is None: @@ -20,21 +21,23 @@ class BeeFactory: def get_connection(self): return BeeFactory.__connection - __nameTranslate = None def getInitNameTranslate(self) -> NameTranslate: - + # (DB<-->Python), + # 1: order_no<-->orderNo + # 2: ORDER_NO<-->orderNo + # 3: original, + # 4: ORDER_NO<-->order_no (DbUpperAndPythonLower) if self.__nameTranslate is None: - # int translateType=HoneyConfig.getHoneyConfig().naming_translateType; - translateType = 1 # TODO from config + translateType = HoneyConfig.naming_translate_type if translateType == 1: __nameTranslate = UnderScoreAndCamelName() elif translateType == 2: __nameTranslate = UpperUnderScoreAndCamelName() elif translateType == 3: __nameTranslate = OriginalName() - elif translateType == 4: __nameTranslate = DbUpperAndJavaLower() + elif translateType == 4: __nameTranslate = DbUpperAndPythonLower() else:__nameTranslate = UnderScoreAndCamelName() - return __nameTranslate; + return __nameTranslate # def __getattribute__(self, item): # print(f"Accessing attribute: {item}") @@ -45,6 +48,3 @@ class BeeFactory: # __honeyFactory = HoneyFactory() # return __honeyFactory - - - diff --git a/src/bee/gen.py b/src/bee/gen.py new file mode 100644 index 0000000..461b171 --- /dev/null +++ b/src/bee/gen.py @@ -0,0 +1,126 @@ +from typing import Dict, Any + +from bee.api import PreparedSql +from bee.config import HoneyConfig +from bee.context import HoneyContext +from bee.name.naming_handler import NamingHandler +from bee.osql import Util +from bee.osql.const import DatabaseConst, SysConst +from bee.osql.struct import TableMeta +from bee.util import HoneyUtil + + +class GenBean: + + _db_sql: Dict[str, Any] = {} + + def __init__(self): + # 初始化 _db_sql(如果尚未初始化) + if not GenBean._db_sql: + self._init_db_sql() + + def gen_bean(self, table_name:str): + sql = self._get_fetch_bean_sql(table_name) + + tableMeta_list = self._get_table_metadata(sql) + # for one in tableMeta_list: + # print(one) + + code = self._metadata_to_bean(table_name, tableMeta_list) + # print(code) + return code + + def gen_and_write_bean(self, table_name:str, file_path: str, file_name:str = None): + code = self.gen_bean(table_name) + if file_name is None: + className = NamingHandler.toEntityName(table_name) + file_name = className + ".py" + Util.write_to_file(file_path, file_name, code) + + def _init_db_sql(self): + sql_mysql = "SELECT COLUMN_NAME col, DATA_TYPE type, CASE IS_NULLABLE WHEN 'YES' THEN 1 ELSE 0 END AS ynNull, CASE COLUMN_KEY WHEN 'PRI' THEN 1 ELSE 0 END AS ynKey, COLUMN_COMMENT label,COLUMN_DEFAULT defaultValue,CHARACTER_MAXIMUM_LENGTH strLen FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '{database}' AND TABLE_NAME = '{table_name}' ORDER BY ORDINAL_POSITION" + sql_sqlite = "select name col,type,[notnull] ynNull,pk ynKey,dflt_value defaultValue from pragma_table_info('{table_name}')" + + GenBean._db_sql[DatabaseConst.MYSQL.lower()] = sql_mysql + GenBean._db_sql[DatabaseConst.SQLite.lower()] = sql_sqlite + + sql_oralce = "SELECT column_name col, data_type type, nullable ynNull, data_default defaultValue, data_length strLen FROM user_tab_columns WHERE table_name = UPPER('{table_name}') ORDER BY column_id" + GenBean.add_fetch_bean_sql(DatabaseConst.ORACLE, sql_oralce) + + def _get_fetch_bean_sql(self, table_name): + + dbName = HoneyContext.get_dbname() + database = HoneyConfig().get_db_config_dict().get('database') + + sql = GenBean._db_sql.get(dbName, None) + if sql is None: + return None + + sql = sql.replace('{table_name}', table_name) + if database: + sql = sql.replace('{database}', database) + + return sql + + @staticmethod + def add_fetch_bean_sql(dbname:str, sql:str): + GenBean._db_sql[dbname.lower()] = sql + + def _get_table_metadata(self, sql): + old_naming_translate_type= HoneyConfig.naming_translate_type + HoneyConfig.naming_translate_type=3 + + pre = PreparedSql() + tableMeta_list = pre.select(sql, TableMeta) + + # reset + HoneyConfig.naming_translate_type=old_naming_translate_type + + return tableMeta_list + + def _metadata_to_bean(self, table_name, tableMeta_list) -> str: + """生成实体类代码""" + dt_set = set() + + className = NamingHandler.toEntityName(table_name) + + class_lines = [ + f"class {className}:", + f" \"\"\" table {table_name} 's entity \"\"\"" + ] + + # 添加字段 + # for col in metadata['columns']: + + for tableMeta in tableMeta_list: + # default = 'None' if tableMeta.ynNull else '' + default = 'None' + fieldName = NamingHandler.toFieldName(tableMeta.col) + fieldType = HoneyUtil.sql_type_to_python_type(tableMeta.type) + + if tableMeta.ynKey: + if fieldName != SysConst.id: + class_lines.insert(2, f" {SysConst.pk} = \"{fieldName}\"") + + if fieldType in ['datetime', 'date', 'time']: + dt_set.add(fieldType) + class_lines.append(f" {fieldName}: {fieldType}") + else: + class_lines.append(f" {fieldName}: {fieldType} = {default}") + + # 添加__repr__方法 + class_lines.extend([ + "", + " def __repr__(self):", + " return str(self.__dict__)" + ]) + + code = "" + if dt_set: + code = "from datetime import " + ", ".join(sorted(dt_set)) + code += "\n\n" + # 生成完整代码 + # code = "\n".join(sorted(imports)) + "\n\n" + code += "\n".join(class_lines) + return code + diff --git a/src/bee/helper.py b/src/bee/helper.py new file mode 100644 index 0000000..bcca21d --- /dev/null +++ b/src/bee/helper.py @@ -0,0 +1,12 @@ +from bee.osql.mid import MidSQL + + +# class SQLAlchemy(MidSQL): +# pass + + +SQLAlchemy = MidSQL # 直接赋值创建别名 + +# 使用示例 +# obj = AliasName() # 效果等同于 OriginalClass() + diff --git a/src/bee/honeyfactory.py b/src/bee/honeyfactory.py index 56f8a71..e4ef031 100644 --- a/src/bee/honeyfactory.py +++ b/src/bee/honeyfactory.py @@ -1,7 +1,10 @@ from bee.api import Suid, SuidRich, PreparedSql -from bee.obj2sql import ObjToSQL -from bee.sqllib import BeeSql + from bee.condition import ConditionImpl +from bee.osql.obj2sql import ObjToSQL +from bee.osql.sqllib import BeeSql + + class HoneyFactory: __instance = None @@ -66,7 +69,8 @@ class HoneyFactory: def setCondition(self, condition): if condition is not None: - self.condition = condition.clone() + # self.condition = condition.clone() + self.condition = condition else: self.condition = condition diff --git a/src/bee/name/NameCheckUtil.py b/src/bee/name/NameCheckUtil.py index f2f6e07..969397f 100644 --- a/src/bee/name/NameCheckUtil.py +++ b/src/bee/name/NameCheckUtil.py @@ -4,6 +4,7 @@ from bee.osql.logger import Logger # NameCheckUtil.py + def is_valid_name(name): pattern = r'^[a-zA-Z]{1}[0-9a-zA-Z_.]*$' return bool(re.match(pattern, name)) @@ -13,17 +14,16 @@ def is_not_valid_name(name): return not is_valid_name(name) -def check_field(name): +def check_fields(name): if name and ',' in name: names = name.split(',') for n in names: - _check_name(n.strip()) + _check_one_name(n.strip()) else: - _check_name(name) - + _check_one_name(name) -def _check_name(name): - if name is None: +def _check_one_name(name): + if not name: raise BeeErrorNameException("The name is null.") if name.lower() == "count(*)": @@ -49,7 +49,7 @@ def is_key_name(name): def is_illegal(field_name): - if (field_name is None or + if (not field_name or ' ' in field_name or '-' in field_name or '#' in field_name or diff --git a/src/bee/name/NameUtil.py b/src/bee/name/NameUtil.py index 56af05b..69c69ae 100644 --- a/src/bee/name/NameUtil.py +++ b/src/bee/name/NameUtil.py @@ -1,5 +1,6 @@ # NameUtil.py + def getClassName(obj): return obj.__class__.__name__ @@ -29,8 +30,9 @@ def toCamelNaming(name): def firstLetterToUpper(name): if not name: return name - # return name[0].upper() + name[1:] - return name.capitalize() + return name[0].upper() + name[1:] + #bug testName->Testname + # return name.capitalize() def firstLetterToLower(name): @@ -38,16 +40,18 @@ def firstLetterToLower(name): return name return name[0].lower() + name[1:] - # if __name__=="__main__": -# -# print(toCamelNaming("_aaa_bb_")) -# print(toCamelNaming("_aaa_bb")) -# +# # +# # print(toCamelNaming("_aaa_bb_")) +# # print(toCamelNaming("_aaa_bb")) +# # # print(toCamelNaming("user_name")) -# -# print(toUnderscoreNaming("userName")) -# print(toUnderscoreNaming("UserName")) -# -# print(firstLetterToUpper("user")) +# print(toCamelNaming("test_name")) +# name="testName" +# print(name.capitalize()) +# # +# # print(toUnderscoreNaming("userName")) +# # print(toUnderscoreNaming("UserName")) +# # +# print(firstLetterToUpper("testName")) diff --git a/src/bee/name/naming.py b/src/bee/name/naming.py index 3789d36..e924e2c 100644 --- a/src/bee/name/naming.py +++ b/src/bee/name/naming.py @@ -1,3 +1,4 @@ +from bee.config import HoneyConfig from bee.name import NameUtil @@ -21,6 +22,8 @@ class NameTranslate: * @author Kingstar * @since 1.5 """ + + class UnderScoreAndCamelName(NameTranslate): def toTableName(self, entityName): @@ -36,15 +39,15 @@ class UnderScoreAndCamelName(NameTranslate): def toEntityName(self, tableName): if not tableName: return tableName - naming_to_lower_before = True # TODO + naming_to_lower_before = HoneyConfig.naming_to_lower_before if naming_to_lower_before: - tableName = tableName.lower() + tableName = tableName.lower() return NameUtil.firstLetterToUpper(NameUtil.toCamelNaming(tableName)) def toFieldName(self, columnName): if not columnName: return columnName - naming_to_lower_before = True # TODO + naming_to_lower_before = HoneyConfig.naming_to_lower_before if naming_to_lower_before: columnName = columnName.lower() return NameUtil.toCamelNaming(columnName) @@ -55,25 +58,25 @@ class OriginalName(NameTranslate): def toTableName(self, entityName): if not entityName: return entityName - return NameUtil.firstLetterToLower(entityName); + return NameUtil.firstLetterToLower(entityName) def toColumnName(self, fieldName): if not fieldName: return fieldName - return fieldName; + return fieldName def toEntityName(self, tableName): if not tableName: return tableName - return NameUtil.firstLetterToUpper(tableName); + return NameUtil.firstLetterToUpper(tableName) def toFieldName(self, columnName): if not columnName: return columnName - return columnName; + return columnName -class DbUpperAndJavaLower(NameTranslate): +class DbUpperAndPythonLower(NameTranslate): def toTableName(self, entityName): if not entityName: @@ -101,20 +104,22 @@ class DbUpperAndJavaLower(NameTranslate): * @author Kingstar * @since 1.5 """ + + class UpperUnderScoreAndCamelName(UnderScoreAndCamelName): def toTableName(self, entityName): - return super.toTableName(entityName).upper(); + return super.toTableName(entityName).upper() def toColumnName(self, fieldName): - return super.toColumnName(fieldName).upper(); + return super.toColumnName(fieldName).upper() def toEntityName(self, tableName): # need lower first if the name has upper - tableName = tableName.lower(); - return NameUtil.firstLetterToUpper(NameUtil.toCamelNaming(tableName)); + tableName = tableName.lower() + return NameUtil.firstLetterToUpper(NameUtil.toCamelNaming(tableName)) def toFieldName(self, columnName): # need lower first if the name has upper - columnName = columnName.lower(); # if not , BEE_NAME->BEENAME -> ?? - return NameUtil.toCamelNaming(columnName); + columnName = columnName.lower() # if not , BEE_NAME->BEENAME -> ?? + return NameUtil.toCamelNaming(columnName) diff --git a/src/bee/name/naming_handler.py b/src/bee/name/naming_handler.py index db73cec..be0e21b 100644 --- a/src/bee/name/naming_handler.py +++ b/src/bee/name/naming_handler.py @@ -6,7 +6,7 @@ class NamingHandler: @staticmethod def getNameTranslate() -> NameTranslate: - # TODO 下一步,要支持使用实时命名规则 + # todo 下一步,要支持使用实时命名规则 factory = BeeFactory() return factory.getInitNameTranslate() diff --git a/src/bee/SqlUtil.py b/src/bee/osql/SqlUtil.py similarity index 50% rename from src/bee/SqlUtil.py rename to src/bee/osql/SqlUtil.py index 950510c..a1fcaa2 100644 --- a/src/bee/SqlUtil.py +++ b/src/bee/osql/SqlUtil.py @@ -1,46 +1,52 @@ import re -from bee.paging import Paging -# SqlUtil.py +from bee.context import HoneyContext + +from bee.osql.paging import Paging +# SqlUtil.py def add_paging(sql, start, size): - if start is None and size is None: + if (not start and start != 0) and not size: return sql - if start is None: + if not start: start = 0 - if size is None: - size = 100 #todo get from config + if not size: + size = 100 # todo get from config paging = Paging() sql = paging.to_page_sql(sql, start, size) return sql -def transform_sql(sql, params_dict=None): - if params_dict is None: + +def transform_sql(sql, params_dict = None): + if not params_dict: return sql # params_dict = {} # 用于存储处理后的 SQL 查询和参数 - para_array = [] + para_array = [] + + placeholder = HoneyContext.get_placeholder() # 用正则表达式匹配所有类似 #{variable} 的模式 - def replace_placeholder(match): + def replace_placeholder(match): # 提取变量名 var_name = match.group(1) # 将变量名添加到参数数组 para_array.append(var_name) - return '?' + # return '?' # bug fixed V1.6.0 + return placeholder # 使用正则替换查询中的变量 sql_transformed = re.sub(r'#\{(\w+)\}', replace_placeholder, sql) - # 从 params_dict 中获取参数值,生成参数元组 - params_tuple = tuple(params_dict[var] for var in para_array) + # 从 params_dict 中获取参数值 + params = [params_dict[var] for var in para_array] - return sql_transformed, params_tuple + return sql_transformed, params diff --git a/src/bee/osql/Util.py b/src/bee/osql/Util.py new file mode 100644 index 0000000..3319275 --- /dev/null +++ b/src/bee/osql/Util.py @@ -0,0 +1,48 @@ +import hashlib +import os +import time + +from bee.osql.logger import Logger + + +def currentMilliseconds(): + # print(time.time()) 浮点数,精度通常为微秒级 + return int(time.time() * 1000) + + +def string_to_md5(text): + # 创建 MD5 哈希对象 + md5_hash = hashlib.md5() + + # 更新哈希对象,需要将字符串编码为字节 + md5_hash.update(text.encode('utf-8')) + + # 获取十六进制格式的哈希值 + return md5_hash.hexdigest() + + +def write_to_file(file_path: str, file_name:str, content: str) -> None: + """将字符串内容写入到指定文件中。 + + Args: + file_path (str): 文件的完整路径 + file_name (str):文件名 + content (str): 要写入文件的字符串内容 + """ + try: + path_and_name = os.path.join(file_path, file_name) # 工程根目录下 + with open(path_and_name, 'w') as file: # 以写入模式打开文件 + file.write(content) # 写入内容 + Logger.info(f"write successful. {path_and_name}") + except Exception as e: + Logger.warn(f"write error: {e}") + + +# # 使用示例 +# if __name__ == "__main__": +# # 指定文件路径和内容 +# file_path = 'output.txt' +# content = 'Hello, this is a test string to be written to the file. 23232' +# +# # 调用方法写入文件 +# write_to_file("E:\\xxx\\tests", "entity.py", content) diff --git a/src/bee/base.py b/src/bee/osql/base.py similarity index 34% rename from src/bee/base.py rename to src/bee/osql/base.py index 810e53b..a397e5c 100644 --- a/src/bee/base.py +++ b/src/bee/osql/base.py @@ -1,58 +1,92 @@ from datetime import datetime import threading +from typing import Any -from bee.osql.enum import SuidType +from bee.config import HoneyConfig +from bee.context import HoneyContext from bee.osql.logger import Logger +from bee.osql.bee_enum import SuidType, LocalType +from bee.osql.cache import CacheUtil +from bee.osql.transform import ParamUtil + + class AbstractCommOperate: - def __init__(self): + def __init__(self): + # HoneyConfig() # how to call first time self.local = threading.local() # 初始化线程本地存储 def doBeforePasreEntity(self, entity, SuidType:SuidType): - #子类在调用此方法时,记录当前的时间,_bee_base_t1 - show_sql_spent_time = False #TODO config - if show_sql_spent_time: - self.local._bee_base_t1 = datetime.now() + # 子类在调用此方法时,记录当前的时间,_bee_base_t1 + self.__reg_start_spent_time() def doBeforePasreListEntity(self, entityArray, SuidType:SuidType): - show_sql_spent_time = False #TODO config - if show_sql_spent_time: - self.local._bee_base_t1 = datetime.now() + self.__reg_start_spent_time() def doBeforeReturn(self, list_param:list): self.__spent_time() - pass + self.__removeCacheSuidStruct() def doBeforeReturnSimple(self): self.__spent_time() - pass - + self.__removeCacheSuidStruct() + + def __removeCacheSuidStruct(self): + HoneyContext._remove_one_local_type(LocalType.CacheSuidStruct) + + def __reg_start_spent_time(self): + if HoneyConfig.show_sql_spent_time: + self.local._bee_base_t1 = datetime.now() + def __spent_time(self): - show_sql_spent_time = False #TODO config + show_sql_spent_time = HoneyConfig.show_sql_spent_time if not show_sql_spent_time: return - #子类在调用此方法时,记录当前的时间,p_t2 - #并获取在doBeforePasreEntity时记录的t1时间,然后计算(p_t2-p_t1)的时间,作为运行消耗的时间。 - if not hasattr(self.local, '_bee_base_t1'): + if not hasattr(self.local, '_bee_base_t1'): Logger.warn("Do not call doBeforeParseEntity, do not register the start time") - else: + else: p_t1 = self.local._bee_base_t1 p_t2 = datetime.now() - # spent_time = (p_t2 - p_t1).total_seconds()*1000 spent_time = int((p_t2 - p_t1).total_seconds() * 1000) - # spent_time = (p_t2 - p_t1).microseconds - Logger.info(f"spent time: {spent_time} ms") + if spent_time > HoneyConfig.show_sql_spent_time_min_ms: + Logger.info(f"spent time: {spent_time} ms") del self.local._bee_base_t1 - # def doAfterCompleteSql(self, sql): # pass + def logsql(self, hard_str, sql): + if not HoneyConfig.show_sql: + return + Logger.logsql(hard_str, sql) + def log_params(self, params): - show_sql_params = True #TODO config - if not show_sql_params: + if not HoneyConfig.show_sql or not HoneyConfig.show_sql_params: + return + params = ParamUtil.transform_param(params) # 1.6.0 + if params is None: return Logger.logsql("params:", params) + + def log_params_for_batch(self, params): + if not HoneyConfig.show_sql_params: + return + # params=ParamUtil.transform_param(params) #1.6.0 + params = ParamUtil.transform_list_tuple_param(params) # 1.6.0 + Logger.logsql("params:", params) + + +class AbstractBase: + + def addInCache(self, sql:str, rs:Any, resultSetSize:int): + if resultSetSize >= HoneyConfig.cache_donot_put_cache_result_min_size: + return + CacheUtil.add(sql, rs) + + def loginfo(self, msg): + if not HoneyConfig.show_sql: + return + Logger.info(msg) diff --git a/src/bee/osql/enum.py b/src/bee/osql/bee_enum.py similarity index 72% rename from src/bee/osql/enum.py rename to src/bee/osql/bee_enum.py index 753e906..1c8c07f 100644 --- a/src/bee/osql/enum.py +++ b/src/bee/osql/bee_enum.py @@ -1,6 +1,6 @@ -from enum import Enum +from enum import Enum, auto -from bee.config import PreConfig +from bee.config import HoneyConfig # class EnumCaseMeta(EnumMeta): @@ -13,7 +13,6 @@ from bee.config import PreConfig # else: # return enum_member._name_.lower() # return value - # class FunctionType(Enum, metaclass=EnumCaseMeta): class FunctionType(Enum): MAX = "max" @@ -25,9 +24,9 @@ class FunctionType(Enum): # def get_name(self): # return self.value def get_name(self): - if PreConfig.sql_key_word_case == "upper": + if HoneyConfig.sql_key_word_case == "upper": return self.value.upper() - else: + else: return self.value.lower() @@ -43,9 +42,6 @@ class SuidType(Enum): def __init__(self, type_string): self.type = type_string - # def getType(self): # 为了更贴近 Java 的 getter 方法命名 - # return self.type - # @property # 或者使用 property 装饰器 def get_name(self): return self.value @@ -57,15 +53,16 @@ class OrderType(Enum): # def get_name(self): # return self.value - def get_name(self): - if PreConfig.sql_key_word_case == "upper": + def get_name(self): + if HoneyConfig.sql_key_word_case == "upper": return self.value.upper() - else: + else: return self.value.lower() def __str__(self): return self.get_name() + class Op(Enum): eq = "=" gt = ">" @@ -87,5 +84,13 @@ class Op(Enum): return self.value def __str__(self): - return self.get_name() + return self.get_name() + + +class LocalType(Enum): + """数据类型标识枚举""" + CacheSuidStruct = auto() # 对应原来的 sqlPreValueLocal + # SQL_PRE_VALUE = auto() # 对应原来的 sqlPreValueLocal + # SQL_INDEX = auto() # 对应原来的 sqlIndexLocal + # CONDITION = auto() # 对应原来的 conditionLocal diff --git a/src/bee/osql/cache.py b/src/bee/osql/cache.py new file mode 100644 index 0000000..9de519b --- /dev/null +++ b/src/bee/osql/cache.py @@ -0,0 +1,404 @@ +import array +import copy +import threading +from typing import Any, Dict, List, Set + +from bee.config import HoneyConfig +from bee.context import HoneyContext +from bee.osql import Util +from bee.osql.logger import Logger + +from bee.osql.bee_enum import LocalType +from bee.osql.index import CacheArrayIndex + + +class CacheUtil: + + # 一般缓存 + __map: Dict[str, int] = {} # + # __obj: List[Any] = [] # 存放缓存对象 + # __keys: List[str] = [] # 保存key的数组 + # __time: List[float] = [] # 存放时间点 + __obj = None # 存放缓存对象 + __keys = None # 保存key的数组 + __time = None # 存放当前毫秒的时间数组 + + __table_indexSet_map: Dict[str, Set] = {} # e.g. {"table_name": {1,2,5} } {'table_name': {indexSet}) + __key_tableNameList_map: Dict[str, List] = {} # e.g. {"key": ['table1','table2'] + + # 特殊缓存 + _set1: Set[str] = set() # 从不放缓存的表名集合 + _set2: Set[str] = set() # 永久放缓存的表名集合 //改成外部不可访问 + _set3: Set[str] = set() # 长久放缓存的表名集合 + + __map2: Dict[str, Any] = {} # 永久缓存 + __map3: Dict[str, Any] = {} # 长久缓存 (有修改时,会删除缓存,下次再获取就会是新的) + + __set2_exist: Set[str] = set() # 已放入永久缓存的key集合 + __set3_exist: Set[str] = set() # 已放入长久缓存的key集合 + __map3_table_keySet: Dict[str, Set] = {} + + __lock = threading.Lock() # 用于线程安全 + __cache_init = False + __MAX_SIZE = None + # __cacheArrayIndex = CacheArrayIndex() #不可以在属性就new,否则import SuidRich时,就会运行CacheArrayIndex的__init__方法 + __cacheArrayIndex = None + + @classmethod + def __init0(cls): + if not cls.__cache_init: + with cls.__lock: + if not cls.__cache_init: + _init_Max_Size = HoneyConfig.cache_max_size + cls.__MAX_SIZE = _init_Max_Size + cls.__keys = Array(_init_Max_Size) + cls.__time = LongArray(_init_Max_Size) + cls.__obj = Array(_init_Max_Size) + + cls.__cacheArrayIndex = CacheArrayIndex() + + # todo 对map和set初始化 + + cls.__cache_init = True + + # 不允许被继承 + def __init_subclass__(self): + raise TypeError("CacheUtil cannot be subclassed") + + @staticmethod + def __get_table_name(sql: str) -> str: + # TODO 可能有多个table 用逗号分隔?? + + cacheSuidStruct = HoneyContext.get_data(LocalType.CacheSuidStruct, sql) + if cacheSuidStruct: + return cacheSuidStruct.tableNames + else: + return None + + @staticmethod + def __genKey(sql: str) -> str: + + cacheSuidStruct = HoneyContext.get_data(LocalType.CacheSuidStruct, sql) + k = None + if cacheSuidStruct: + msg = f"{sql} # {str(cacheSuidStruct.params)} # {cacheSuidStruct.returnType} # {str(cacheSuidStruct.entityClass)} # {cacheSuidStruct.suidType.get_name()}" + k = msg + else: + k = sql + + if HoneyConfig.cache_key_use_md5 == True: + return Util.string_to_md5(k) + else: + return k + + @staticmethod + def add(sql: str, rs: Any): + + CacheUtil.__init0() + + key = CacheUtil.__genKey(sql) + table_name = CacheUtil.__get_table_name(sql) + + # 1. 检查是否在set1(从不缓存) + if table_name in CacheUtil._set1: + return False + + # 2. 检查是否在set2(永久缓存) + if table_name in CacheUtil._set2: + if key not in CacheUtil.__set2_exist: + CacheUtil.__set2_exist.add(key) + CacheUtil.__map2[key] = rs + return True + + # 3. 检查是否在set3(长久缓存) + if table_name in CacheUtil._set3: + if key not in CacheUtil.__set3_exist: + CacheUtil.__set3_exist.add(key) + CacheUtil.__map3[key] = rs + + CacheUtil.__reg_map3_table_keySet(table_name, key) + return True + + if(CacheUtil.__cacheArrayIndex.is_would_be_full()): + if HoneyConfig.show_sql: + Logger.info(" Cache would be full!") + + # 快满了,删除一定比例最先存入的 + CacheUtil._del_cache_in_between(CacheUtil.__cacheArrayIndex.get_delete_cache_index()) + # 上一句改为新线程运行才启用 TODO + # if CacheUtil.__cacheArrayIndex.get_used_rate() >= 90: + # if HoneyConfig.show_sql: + # Logger.warn("[Bee] ==========Cache already used more than 90% !"); + # return False # 快满了,本次不放缓存,直接返回 + + # 4. 一般缓存 + with CacheUtil.__lock: + index = CacheUtil.__cacheArrayIndex.get_next() + + CacheUtil.__map[key] = index + CacheUtil.__obj[index] = rs + CacheUtil.__keys[index] = key + CacheUtil.__time[index] = Util.currentMilliseconds() + + CacheUtil.__key_tableNameList_map[key] = table_name + + CacheUtil.__reg_table_indexSet_map(table_name, index) + + return True + + # reg index set to table_dict table关联它相关的缓存的下标。 + @staticmethod + def __reg_table_indexSet_map(table_name:str, index:int): + indexSet = CacheUtil.__table_indexSet_map.get(table_name, None) + if indexSet: + indexSet.add(index) + else: + indexSet = set() + indexSet.add(index) + CacheUtil.__table_indexSet_map[table_name] = indexSet + + @staticmethod + def __reg_map3_table_keySet(table_name:str, key:int): + keySet = CacheUtil.__map3_table_keySet.get(table_name, None) + if keySet: + keySet.add(key) + else: + keySet = set() + keySet.add(key) + CacheUtil.__map3_table_keySet[table_name] = keySet + + @staticmethod + def get(sql: str) -> Any: + + CacheUtil.__init0() + + key = CacheUtil.__genKey(sql) + + index = CacheUtil.__map.get(key, None) + if index == 0 or index: + if CacheUtil.__is_timeout(index): + if CacheUtil.__cacheArrayIndex.is_start_delete(): + # 删除多个 TODO 起一个线程 + CacheUtil._del_cache_in_between(index) + else: + CacheUtil.__del_one_cache(index) + + return None + return copy.deepcopy(CacheUtil.__obj[index]) # 返回深拷贝 + + # 检查永久缓存 + if key in CacheUtil.__set2_exist: + return copy.deepcopy(CacheUtil.__map2.get(key)) # 返回深拷贝 + + # 检查长久缓存 + if key in CacheUtil.__set3_exist: + return copy.deepcopy(CacheUtil.__map3.get(key)) # 返回深拷贝 + + return None + + @staticmethod + def __is_timeout(index: int) -> bool: + timeout = HoneyConfig.cache_timeout # 缓存过期时间,以毫秒为单位 + return Util.currentMilliseconds() - CacheUtil.__time[index] >= timeout + + @staticmethod + def __del_one_cache(index: int): + Logger.debug("del one cache, index is: " + str(index)) + key = CacheUtil.__keys[index] + CacheUtil.__del_one_index(key, index) + + CacheUtil.__del_one_normal_cache(index) + + @staticmethod + def __del_one_normal_cache(index: int): + with CacheUtil.__lock: + key = CacheUtil.__keys[index] + if key in CacheUtil.__map: + del CacheUtil.__map[key] + CacheUtil.__time[index] = 0 + CacheUtil.__obj[index] = None + CacheUtil.__keys[index] = None + + @staticmethod + # _del_one_index in tableIndexSet + def __del_one_index(key: str, index: int): + # 这里可以添加维护表相关索引的逻辑 + + table_name = CacheUtil.__key_tableNameList_map[key] + indexSet = CacheUtil.__table_indexSet_map.get(table_name, None) + if indexSet: + indexSet.remove(index) # test + + @staticmethod + def clear(sql: str): + CacheUtil.__init0() + + table_name = CacheUtil.__get_table_name(sql) + indexSet = CacheUtil.__table_indexSet_map.pop(table_name, None) + if indexSet: + for index in indexSet: + CacheUtil.__del_one_normal_cache(index) + else: # __map3 + keySet = CacheUtil.__map3_table_keySet.pop(table_name, None) + if keySet: + for key in keySet: + del CacheUtil.__map3[key] + + @staticmethod + def _del_cache_in_between(know_index: int): + """ + 删除 low 和 know 之间的缓存(包含两端) + :param know_index: 已知超时的索引位置 + """ + max_size = CacheUtil.__MAX_SIZE + array_index = CacheUtil.__cacheArrayIndex + + low = array_index.low + high = array_index.high + know = know_index + + def _delete_cache_by_index(i: int): + CacheUtil.__del_one_cache(i) + + if low <= high: + # 情况1:正常顺序(low <= high) + for i in range(low, know + 1): # 包含 know + _delete_cache_by_index(i) + CacheUtil.__cacheArrayIndex.low = (know + 1) % max_size # 处理循环 + + msg = f"delete cache from {low} to {know}, new low: {CacheUtil.__cacheArrayIndex.low}" + Logger.debug(msg) + else: + # 情况2:循环缓存(low > high) //all:0-99; low 80 know:90 99, 0 20:high + if low < know: + # 子情况2.1:know 在 low 之后(未跨循环点) + for i in range(low, know + 1): + _delete_cache_by_index(i) + CacheUtil.__cacheArrayIndex.low = (know + 1) % max_size + + msg = f"delete cache from {low} to {know}, new low: {CacheUtil.__cacheArrayIndex.low}" + Logger.debug(msg) + + elif know < high: # all:0-99; low 80 90 99, 0 know:10 20:high + # 子情况2.2:know 在 high 之前(跨循环点) + # 删除从 low 到末尾的部分 + for i in range(low, max_size): + _delete_cache_by_index(i) + # 删除从开头到 know 的部分 + for i in range(0, know + 1): + _delete_cache_by_index(i) + CacheUtil.__cacheArrayIndex.low = know + 1 # 自动处理循环(因为 know < high < low) + + CacheUtil.__cacheArrayIndex.low = (know + 1) % max_size + msg = f"delete cache from {low} to {max_size},{know}, new low: {CacheUtil.__cacheArrayIndex.low}" + + # @staticmethod + # def _getMap(): # TEST for test todo + # print("cache map size: ", len(CacheUtil.__map)) + # return CacheUtil.__map + + +class Array: + + __array:None + __size = 0 + + def __init__(self, size:int): + self.__size = size + self.__array = [None] * size + + def __setitem__(self, index:int, v:str): + if index >= self.__size: + Logger.warn("Error index: " + str(index)) + return + self.__array[index] = v + + def __getitem__(self, index): + if index >= self.__size: + Logger.warn("Error index: " + str(index)) + return None + return self.__array[index] + + def __str__(self): + # print(type(self.__array)) # + return "array len:" + str(len(self.__array)) + ", value:" + str(self.__array) + + +class LongArray: + + __array:None + __size = 0 + + def __init__(self, size:int): + self.__size = size + self.__array = array.array("Q", [0] * size) + + def __setitem__(self, index:int, v): + if index >= self.__size: + Logger.warn("Error index: " + str(index)) + return + self.__array[index] = v + + def __getitem__(self, index): + if index >= self.__size: + Logger.warn("Error index: " + str(index)) + return None + return self.__array[index] + + def __str__(self): + return "array len:" + str(len(self.__array)) + ", value:" + str(self.__array) + +# if __name__ == '__main__': +# print("start") +# sa = Array(10) +# sa[5] = "abc" +# +# print(sa) +# print(type(sa)) +# +# long_array = LongArray(10) +# long_array[6] = 6 +# long_array[10] = 10 # test: Error index: 10 +# print(long_array) +# +# # print(K.__sql_keywords) +# +# if __name__ == "__main__": +# # 示例:使用 CacheUtil 管理缓存,并验证深拷贝 +# # 假设有以下 SQL 查询和结果 +# sql1 = "SELECT * FROM users WHERE id = 1" +# result1 = {"id": 1, "name": "Alice"} +# +# print(result1) +# +# # 1. 添加一般缓存 +# CacheUtil.add(sql1, result1) +# print("Added general cache for sql1") +# +# # 2. 获取一般缓存并修改 +# cached_result1 = CacheUtil.get(sql1) +# print("修改前", cached_result1) +# cached_result1["name"] = "Bob" # 修改获取的缓存结果 +# print("Modified cached result for sql1:", cached_result1) +# +# # 3. 再次获取缓存,验证是否被修改 +# original_cached_result1 = CacheUtil.get(sql1) +# print("再次获取,修改前", original_cached_result1) +# print("Original cached result for sql1:", original_cached_result1) +# +# sql1 = "SELECT * FROM orders WHERE id = 1" # orders +# result1 = {"id": 10, "name": "milk"} +# # 4. 添加永久缓存 +# CacheUtil._set2.add("orders") # 将 'orders' 表标记为永久缓存 +# # CacheUtil._set2.add("users") # 将 'orders' 表标记为永久缓存 +# CacheUtil.add(sql1, result1) # 再次添加 sql1,这次会放入永久缓存 +# print("Added permanent cache for sql1") +# +# # 5. 获取永久缓存并修改 +# cached_result1_permanent = CacheUtil.get(sql1) +# cached_result1_permanent["name"] = "apple" # 修改获取的缓存结果 +# print("Modified permanent cached result for sql1:", cached_result1_permanent) +# +# # 6. 再次获取永久缓存,验证是否被修改 +# original_cached_result1_permanent = CacheUtil.get(sql1) +# print("Original permanent cached result for sql1:", original_cached_result1_permanent) diff --git a/src/bee/osql/const.py b/src/bee/osql/const.py index 8c889b3..5ddb404 100644 --- a/src/bee/osql/const.py +++ b/src/bee/osql/const.py @@ -50,7 +50,7 @@ class DatabaseConst: class StrConst: - LOG_PREFIX = "[Bee]=========" + LOG_PREFIX = "[Bee]========" LOG_SQL_PREFIX = "[Bee] sql>>> " @@ -58,12 +58,14 @@ class SysConst: tablename = "__tablename__" pk = "__pk__" primary_key = "__primary_key__" - id="id" + id = "id" - dbModuleName="dbModuleName" - dbName="dbName" + dbModuleName = "dbModuleName" + dbname = "dbname" - configPropertiesFileName="bee.properties" - configJsonFileName="bee.json" + upper = "upper" + + # move to PreConfig + # configPropertiesFileName = "bee.properties" + # configJsonFileName = "bee.json" - upper="upper" diff --git a/src/bee/osql/index.py b/src/bee/osql/index.py new file mode 100644 index 0000000..c282cce --- /dev/null +++ b/src/bee/osql/index.py @@ -0,0 +1,106 @@ +import threading +from typing import Optional + +from bee.config import HoneyConfig + + +class CacheArrayIndex: + _instance_lock = threading.Lock() + _instance: Optional['CacheArrayIndex'] = None + + def __new__(cls): + if cls._instance is None: + # print("--------CacheArrayIndex-----__new__-------") + with cls._instance_lock: + if cls._instance is None: + cls._instance = super().__new__(cls) + # 初始化实例变量 + cls._instance._low = 0 # 低水位线(较旧的数据) + cls._instance._high = 0 # 高水位线 + cls._instance._know = 0 # 已知超时点 + cls._instance._size = None + cls._instance._start_delete_cache_rate = 0.6 + cls._instance._full_used_rate = 0.9 + cls._instance._full_clear_cache_size = None + return cls._instance + + def __init__(self): + # 这些配置应该在首次使用时从配置加载 + if not hasattr(self, '_initialized'): + # print("--------CacheArrayIndex-----__init__-------") + # # HoneyConfig() + # print("--------CacheArrayIndex-----__init__-------,HoneyConfig.cache_start_delete_rate :", HoneyConfig.cache_start_delete_rate) + # print("--------CacheArrayIndex-----__init__-------,HoneyConfig.cache_full_used_rate :", HoneyConfig.cache_full_used_rate) + # print("--------CacheArrayIndex-----__init__-------,HoneyConfig.cache_max_size :", HoneyConfig.cache_max_size) + # print("--------CacheArrayIndex-----__init__-------,HoneyConfig._full_clear_cache_size :", HoneyConfig.cache_full_clear_rate) + + self._start_delete_cache_rate = int(HoneyConfig.cache_start_delete_rate * 100) + self._full_used_rate = int(HoneyConfig.cache_full_used_rate * 100) + self._size = HoneyConfig.cache_max_size + self._full_clear_cache_size = int(HoneyConfig.cache_full_clear_rate * self._size) + self._lock = threading.RLock() + self._initialized = True + + @property + def low(self) -> int: + with self._lock: + return self._low + + @low.setter + def low(self, value: int): + with self._lock: + self._low = value + + @property + def high(self) -> int: + return self._high + + @property + def know(self) -> int: + return self._know + + def get_used_size(self) -> int: + t = self.high - self.low + return t if t >= 0 else t + self._size + + def get_empty_size(self) -> int: + return self._size - self.get_used_size() + + def get_used_rate(self) -> int: + return (self.get_used_size() * 100) // self._size + + def get_next(self) -> int: + with self._lock: + if self._high >= self._size: + self._high = 1 + return 0 + else: + result = self._high + self._high += 1 + return result + + def is_full(self) -> bool: + return self.get_empty_size() == 0 + + def is_would_be_full(self) -> bool: + return self.get_used_rate() >= self._full_used_rate + + def get_delete_cache_index(self) -> int: + return (self.low + self._full_clear_cache_size - 1) % self._size + + def is_start_delete(self) -> bool: + return self.get_used_rate() > self._start_delete_cache_rate + +# 使用示例 +# if __name__ == '__main__': +# +# # aa=HoneyConfig() +# +# # 使用单例 +# array_index = CacheArrayIndex() +# print(array_index.get_next()) # 线程安全的获取下一个索引 +# print(array_index.get_next()) # 线程安全的获取下一个索引 +# +# for i in range(22): +# print(array_index.get_next()) + diff --git a/src/bee/osql/local.py b/src/bee/osql/local.py new file mode 100644 index 0000000..28bf01d --- /dev/null +++ b/src/bee/osql/local.py @@ -0,0 +1,37 @@ +import threading +from typing import Dict, Any, Optional + + +class OneTimeParameter: + + __local = threading.local() + + @staticmethod + def get_attribute(key: str) -> Optional[Any]: + if not hasattr(OneTimeParameter.__local, 'storage'): + return None + storage: Dict[str, Any] = OneTimeParameter.__local.storage + value = storage.pop(key, None) # Remove after getting + return value + + @staticmethod + def set_attribute(key: str, obj: Any) -> None: + if obj is None: + return + if not hasattr(OneTimeParameter.__local, 'storage'): + OneTimeParameter.__local.storage = {} + OneTimeParameter.__local.storage[key] = obj + + @staticmethod + def set_true_for_key(key: str) -> None: + OneTimeParameter.set_attribute(key, "TRUE") # Using string "TRUE" as equivalent + + @staticmethod + def is_true(key: str) -> bool: + value = OneTimeParameter.get_attribute(key) + return value == "TRUE" + + @staticmethod + def remove() -> None: + if hasattr(OneTimeParameter.__local, 'storage'): + del OneTimeParameter.__local.storage diff --git a/src/bee/osql/mid.py b/src/bee/osql/mid.py new file mode 100644 index 0000000..29e188e --- /dev/null +++ b/src/bee/osql/mid.py @@ -0,0 +1,180 @@ +from bee.context import HoneyContext +from bee.gen import TableMeta +from bee.name.naming_handler import NamingHandler +from bee.util import HoneyUtil + +from bee.osql.mid_typing import * + +class Model: + __subclasses__ = [] + + def __init_subclass__(self, **kwargs): + if "" != str(self): + super().__init_subclass__(**kwargs) + Model.__subclasses__.append(self) + self.__table__ = type('Table', (), {'columns': []}) + + def __init__(self): + # 收集所有Column属性 + for name, value in self.__class__.__dict__.items(): + if isinstance(value, Column): + value.name = name + self.__table__.columns.append(value) + + +class MidSQL: + + class Model(Model): + pass + + # 添加类型属性 + Integer = Integer() + SmallInteger = SmallInteger() + SMALLINT = SMALLINT() + BigInteger = BigInteger() + Boolean = Boolean() + + @property + def String(self): + return String + + @property + def Numeric(self): + return Numeric + + @property + def DECIMAL(self): + return DECIMAL + + Text = Text() + + JSON = JSON() + + DateTime = DateTime() + Date = Date() + Time = Time() + + REAL = REAL() + Float = Float() + + def Column(self, *args, **kwargs): + return Column(*args, **kwargs) + + def __default_type(self): + try: + if HoneyContext.isOracle(): + return "VARCHAR2" + else: + return "VARCHAR" + except Exception: + return "VARCHAR" + + def to_create_all_sql(self): + + NOT_NULL_STR = HoneyUtil.adjustUpperOrLower(" NOT NULL") + # NULL_STR = HoneyUtil.adjustUpperOrLower(" NULL") + UNIQUE_STR = HoneyUtil.adjustUpperOrLower(" UNIQUE") + PK_STR = HoneyUtil.adjustUpperOrLower("PRIMARY KEY") + CREATE_TABLE_STR = HoneyUtil.adjustUpperOrLower("CREATE TABLE") + + all_sql = [] + table_names = [] + + for model in Model.__subclasses__: + # print(model) + # 实例化模型以收集列信息 + model_instance = model() + + # unique_list = [] + meta_list = [] # 存储所有TableMeta对象的列表 + addPkLast = None + for column in model_instance.__table__.columns: + # 创建TableMeta对象并填充属性 + meta = TableMeta() + meta.col = column.name + meta.type = str(column.type) + meta.ynNull = column.nullable + meta.ynKey = column.primary_key + meta.unique = column.unique + + # if column.unique: + # unique_list.append(column.unique) + + # 特殊处理String类型的长度 + if isinstance(column.type, String): + meta.strLen = column.type.length + + if isinstance(column.type, Numeric): + meta.precision = column.type.precision + meta.scale = column.type.scale + + meta_list.append(meta) + + sql_fields = [] + sql_statement = "" + for meta in meta_list: + column = NamingHandler.toColumnName(meta.col) + col_type = HoneyUtil.adjustUpperOrLower(meta.type) + + temp_sql = "" + + if meta.ynKey: + temp_type = HoneyUtil.generate_pk_statement() + temp_sql = column + temp_type + if " int(11)" == temp_type: + addPkLast = column + temp_sql += NOT_NULL_STR + temp_sql = HoneyUtil.adjustUpperOrLower(temp_sql) + elif meta.type == 'String': + # col_type = HoneyUtil.adjustUpperOrLower(self.__default_type()) + col_type = HoneyUtil.adjustUpperOrLower(HoneyUtil.mid_type_to_sql_type(meta.type)) + if not meta.strLen: + meta.strLen=255 + col_type += '(' + str(meta.strLen) + ')' + temp_sql = f"{column} {col_type}" + elif meta.type == 'Text': + + if meta.strLen: + col_type += '(' + meta.strLen + ')' + temp_sql = f"{column} {col_type}" + else: + col_type = HoneyUtil.adjustUpperOrLower(HoneyUtil.mid_type_to_sql_type(meta.type)) + temp_sql = f"{column} {col_type}" + if meta.type == 'Numeric': + temp_sql += f"({meta.precision},{meta.scale})" + + if meta.unique: + temp_sql += UNIQUE_STR + + if not meta.ynKey: + if meta.ynNull: + # temp_sql += NULL_STR + pass + else: + temp_sql += NOT_NULL_STR + + sql_fields.append(temp_sql) + + if addPkLast: + sql_fields.append(f"{PK_STR}({addPkLast})") + + sql_statement = f"{CREATE_TABLE_STR} {model.__name__} (\n " + ",\n ".join(sql_fields) + "\n);" + # print(sql_statement) + all_sql.append(sql_statement) + table_names.append(model.__name__) + return all_sql, table_names + + # def create_all(self, is_drop_exist_table = None): + # + # all_sql, table_names = self.to_create_all_sql() + # pre = PreparedSql() + # + # if is_drop_exist_table: + # objToSQL = ObjToSQL() + # for tab_name in table_names: + # sql0 = objToSQL.toDropTableSQL(tab_name) + # pre.modify(sql0) + # + # for sql in all_sql: + # pre.modify(sql) + diff --git a/src/bee/osql/mid_typing.py b/src/bee/osql/mid_typing.py new file mode 100644 index 0000000..132e17f --- /dev/null +++ b/src/bee/osql/mid_typing.py @@ -0,0 +1,104 @@ +class Column: + + def __init__(self, type_, primary_key = False, unique = False, nullable = True): + self.type = type_ + self.primary_key = primary_key + self.unique = unique + self.nullable = nullable + self.name = None # 会在Model的__init__中设置 + + +class Integer: + + def __repr__(self): + return "Integer" + + +class SmallInteger: + + def __repr__(self): + return "SmallInteger" + + +# class SmallInt: +# +# def __repr__(self): +# return "SmallInt" + + +class BigInteger: + + def __repr__(self): + return "BigInteger" + + +class Boolean: + + def __repr__(self): + return "Boolean" + + +class String: + + def __init__(self, length = None): + self.length = length + + def __repr__(self): + # return f"String({self.length})" + return f"String" + + +class Text: + + def __repr__(self): + return "Text" + + +class JSON: + + def __repr__(self): + return "JSON" + + +class DateTime: + + def __repr__(self): + return "DateTime" + + +class Date: + + def __repr__(self): + return "Date" + + +class Time: + + def __repr__(self): + return "Time" + + +class Float: + + def __repr__(self): + return "Float" + +class Numeric: + def __init__(self, precision, scale): + self.precision = precision # 总位数(如10) + self.scale = scale # 小数位数(如2) + + def __repr__(self): + # return f"Numeric({self.precision}, {self.scale})" + return "Numeric" + +# 同 Numeric,别名 +DECIMAL = Numeric + +SMALLINT = SmallInteger + + +class REAL: + + def __repr__(self): + return "REAL" \ No newline at end of file diff --git a/src/bee/obj2sql.py b/src/bee/osql/obj2sql.py similarity index 64% rename from src/bee/obj2sql.py rename to src/bee/osql/obj2sql.py index 3a70ff1..db1e747 100644 --- a/src/bee/obj2sql.py +++ b/src/bee/osql/obj2sql.py @@ -1,20 +1,20 @@ -from bee import SqlUtil from bee.config import HoneyConfig from bee.context import HoneyContext from bee.exception import SqlBeeException, ParamBeeException from bee.name import NameCheckUtil from bee.name.naming_handler import NamingHandler +from bee.osql import SqlUtil from bee.osql.const import DatabaseConst, SysConst -from bee.osql.enum import SuidType from bee.osql.logger import Logger from bee.osql.sqlkeyword import K from bee.util import HoneyUtil from bee.condition import Condition +from bee.osql.bee_enum import SuidType class ObjToSQL: - + def toSelectSQL(self, entity): fieldAndValue, classField = self.__getKeyValue_classField(entity) @@ -28,22 +28,22 @@ class ObjToSQL: return self.__build_select_sql2(table_name, classField, fieldAndValue, condition) def toSelectSQLWithPaging(self, entity, start, size): - sql, params = self.toSelectSQL(entity) + sql, params, table_name = self.toSelectSQL(entity) sql = SqlUtil.add_paging(sql, start, size) - return sql, params + return sql, params, table_name def toUpdateSQL(self, entity): fieldAndValue = self.__getKeyValue(entity) pk = HoneyUtil.get_pk(entity) - if pk is None: + if not pk: if SysConst.id in fieldAndValue: - pk=SysConst.id + pk = SysConst.id else: raise SqlBeeException("update by id, bean should has id field or need set the pk field name with __pk__") pkvalue = fieldAndValue.pop(pk, None) - if pkvalue is None: + if not pkvalue: raise SqlBeeException("the id/pk value can not be None") conditions = {pk:pkvalue} @@ -53,7 +53,7 @@ class ObjToSQL: def toUpdateBySQL2(self, entity, condition, whereFields): fieldAndValue, classField = self.__getKeyValue_classField(entity) - ext=list(set(whereFields) - set(classField)) + ext = list(set(whereFields) - set(classField)) if ext: raise ParamBeeException("some fields in whereFields not in bean: ", ext) @@ -65,7 +65,6 @@ class ObjToSQL: fieldAndValue = self.__getKeyValue(entity) return self.__build_insert_sql(table_name, fieldAndValue) - def toDeleteSQL(self, entity): table_name = HoneyUtil.get_table_name(entity) fieldAndValue = self.__getKeyValue(entity) @@ -80,12 +79,12 @@ class ObjToSQL: table_name = HoneyUtil.get_table_name(entity_list[0]) # fieldAndValue = self.__getKeyValue(entity_list[0]) - cls=type(entity_list[0]) + cls = type(entity_list[0]) classField = HoneyUtil.get_class_field(cls) - sql=self.__build_insert_batch_sql(table_name, classField) - list_params= HoneyUtil.get_list_params(classField, entity_list) + sql = self.__build_insert_batch_sql(table_name, classField) + list_params = HoneyUtil.get_list_params(classField, entity_list) - return sql, list_params + return sql, list_params, table_name def toSelectByIdSQL(self, entity_class, length): classField = HoneyUtil.get_class_field(entity_class) # list @@ -93,12 +92,12 @@ class ObjToSQL: table_name = HoneyUtil.get_table_name_by_class(entity_class) - return self.__build_select_by_id_sql(table_name, classField, where_condition_str) + return self.__build_select_by_id_sql(table_name, classField, where_condition_str), table_name def _toWhereById(self, entity_class, length): pk = HoneyUtil.get_pk_by_class(entity_class) - if pk is None: + if not pk: raise SqlBeeException("by id, bean should has id field or need set the pk field name with __pk__") pk = NamingHandler.toColumnName(pk) @@ -114,23 +113,23 @@ class ObjToSQL: def toDeleteById(self, entity_class, length): where_condition_str = self._toWhereById(entity_class, length) table_name = HoneyUtil.get_table_name_by_class(entity_class) - return self.__build_delete_by_id_sql(table_name, where_condition_str) - - def _toById(self, entity): - fieldAndValue, classField = self.__getKeyValue_classField(entity) - pk = HoneyUtil.get_pk(entity) - if pk is None: - if SysConst.id in fieldAndValue: - pk = SysConst.id - else: - raise SqlBeeException("by id, bean should has id field or need set the pk field name with __pk__") - - pkvalue = fieldAndValue.pop(pk, None) - if pkvalue is None: - raise SqlBeeException("the id/pk value can not be None") - - conditions = {pk:pkvalue} - return classField, conditions + return self.__build_delete_by_id_sql(table_name, where_condition_str), table_name + + # def _toById(self, entity): + # fieldAndValue, classField = self.__getKeyValue_classField(entity) + # pk = HoneyUtil.get_pk(entity) + # if pk is None: + # if SysConst.id in fieldAndValue: + # pk = SysConst.id + # else: + # raise SqlBeeException("by id, bean should has id field or need set the pk field name with __pk__") + # + # pkvalue = fieldAndValue.pop(pk, None) + # if pkvalue is None: + # raise SqlBeeException("the id/pk value can not be None") + # + # conditions = {pk:pkvalue} + # return classField, conditions def toSelectFunSQL(self, entity, functionType, field_for_fun): fieldAndValue, _ = self.__getKeyValue_classField(entity) @@ -142,10 +141,10 @@ class ObjToSQL: fieldAndValue, _ = self.__getKeyValue_classField(entity) return fieldAndValue - #__ or _只是用于范围保护,转成sql时,将这两种前缀统一去掉 + # __ or _只是用于范围保护,转成sql时,将这两种前缀统一去掉 def __getKeyValue_classField(self, entity): - cls=type(entity) + cls = type(entity) classField = HoneyUtil.get_class_field(cls) # list fieldAndValue = HoneyUtil.get_obj_field_value(entity) # dict @@ -177,7 +176,7 @@ class ObjToSQL: if value is not None and fieldAndValue.get(name, None) is None: fieldAndValue[name] = value - #排除value为None的项 + # 排除value为None的项 fieldAndValue = {key: value for key, value in fieldAndValue.items() if value is not None} return fieldAndValue, classField @@ -185,7 +184,7 @@ class ObjToSQL: def __getPlaceholder(self): return HoneyContext.get_placeholder() - #updateById + # updateById def __build_update_sql(self, table_name, set_dict, entityFilter): # entityFilter just pk if not set_dict: @@ -197,7 +196,7 @@ class ObjToSQL: set_dict2 = self.__toColumns_with_dict(set_dict) conditions2 = self.__toColumns_with_dict(entityFilter) - ph=self.__getPlaceholder() + ph = self.__getPlaceholder() if self.__getPlaceholderType() == 3: updateSet = ', '.join(f"{key} = {ph}{key}" for key in set_dict2.keys()) condition_str = f" {K.and_()} ".join(f"{key} = {ph}{key}" for key in conditions2.keys()) @@ -208,8 +207,7 @@ class ObjToSQL: # sql = f"UPDATE {table_name} SET {updateSet} WHERE {condition_str}" sql = f"{K.update()} {table_name} {K.set()} {updateSet} {K.where()} {condition_str}" params = list(set_dict2.values()) + list(conditions2.values()) - return sql, params - + return sql, params, table_name def __build_insert_sql(self, table_name, data): if not data: @@ -217,22 +215,22 @@ class ObjToSQL: data2 = self.__toColumns_with_dict(data) - ph=self.__getPlaceholder() + ph = self.__getPlaceholder() columns = ', '.join(data2.keys()) if self.__getPlaceholderType() == 3: placeholders = ', '.join(f" {ph}{key}" for key in data2.keys()) else: - placeholders = ', '.join(f"{ph}" for _ in data2) #TODO + placeholders = ', '.join(f"{ph}" for _ in data2) sql = f"{K.insert()} {K.into()} {table_name} ({columns}) {K.values()} ({placeholders})" - return sql, list(data2.values()) + return sql, list(data2.values()), table_name def __build_insert_batch_sql(self, table_name, classField): if not classField: raise SqlBeeException("column list is empty!") - columns=self.__toColumns(classField) + columns = self.__toColumns(classField) - ph=self.__getPlaceholder() + ph = self.__getPlaceholder() columnsStr = ', '.join(columns) if self.__getPlaceholderType() == 3: placeholders = ', '.join(f" {ph}{item}" for item in classField) @@ -242,6 +240,8 @@ class ObjToSQL: return sql def __toColumns_with_dict(self, kv): + if not kv: + return None return {NamingHandler.toColumnName(k):v for k, v in kv.items()} def __build_where_condition(self, entityFilter): @@ -257,135 +257,170 @@ class ObjToSQL: def __toColumns(self, classField): return [NamingHandler.toColumnName(field) for field in classField] - def __build_select_sql(self, table_name, classField, entityFilter=None): + def __build_select_sql(self, table_name, classField, entityFilter = None): if not classField: raise SqlBeeException("column list is empty!") - columns=self.__toColumns(classField) + columns = self.__toColumns(classField) + # columns="*" #TODO TEST # sql = f"SELECT * FROM {table_name}" # sql = f"SELECT {', '.join(classField)} FROM {table_name}" sql = f"{K.select()} {', '.join(columns)} {K.from_()} {table_name}" - #where part + # where part params = [] if entityFilter: - condition_str=self.__build_where_condition(entityFilter) + condition_str = self.__build_where_condition(entityFilter) sql += f" {K.where()} {condition_str}" params = list(entityFilter.values()) - return sql, params + return sql, params, table_name - def __build_select_sql2(self, table_name, classField, entityFilter=None, condition=None): + def __build_select_sql2(self, table_name, classField, entityFilter = None, condition = None): if condition: - conditionStruct=condition.parseCondition() - selectFields=conditionStruct.selectFields + conditionStruct = condition.parseCondition() + selectFields = conditionStruct.selectFields if not selectFields and not classField: raise SqlBeeException("column list is empty!") if selectFields: - columns=self.__toColumns(selectFields) # TODO + columns = self.__toColumns(selectFields) else: - columns=self.__toColumns(classField) + columns = self.__toColumns(classField) sql = f"{K.select()} {', '.join(columns)} {K.from_()} {table_name}" - #where part + # where part params = [] if entityFilter: - condition_str=self.__build_where_condition(entityFilter) + condition_str = self.__build_where_condition(entityFilter) sql += f" {K.where()} {condition_str}" params = list(entityFilter.values()) if conditionStruct: - sql,params=self.__appendWhere(sql, params, entityFilter, conditionStruct) + sql, params = self.__appendWhere(sql, params, entityFilter, conditionStruct) start = conditionStruct.start size = conditionStruct.size - if start or size: + if start or start == 0 or size: sql = SqlUtil.add_paging(sql, start, size) if conditionStruct.has_for_update: sql += " " + K.for_update() - return sql, params + return sql, params, table_name - def __appendWhere(self,sql,params,entityFilter,conditionStruct): + def __appendWhere(self, sql, params, entityFilter, conditionStruct): if conditionStruct: - condition_where=conditionStruct.where + condition_where = conditionStruct.where if condition_where: - values=conditionStruct.values + values = conditionStruct.values if entityFilter: - sql += " "+ K.and_() + sql += " " + K.and_() else: - sql +=" "+ K.where() - sql +=" "+condition_where + sql += " " + K.where() + sql += " " + condition_where params = params + values - return sql,params + return sql, params - def __build_delete_sql2(self, table_name, entityFilter, condition=None): + def __build_delete_sql2(self, table_name, entityFilter, condition = None): sql = f"{K.delete()} {K.from_()} {table_name}" params = [] if entityFilter: - condition_str=self.__build_where_condition(entityFilter) + condition_str = self.__build_where_condition(entityFilter) sql += f" {K.where()} {condition_str}" params = list(entityFilter.values()) if condition: condition.suidType(SuidType.DELETE) - conditionStruct=condition.parseCondition() - sql,params=self.__appendWhere(sql, params, entityFilter, conditionStruct) + conditionStruct = condition.parseCondition() + sql, params = self.__appendWhere(sql, params, entityFilter, conditionStruct) - return sql, params + return sql, params, table_name def __build_update_by_sql2(self, table_name, entityFilter, condition, whereFields): - where_dict = {} - set_dict = {} - for key, value in entityFilter.items(): - if key in whereFields: - where_dict[key] = value - else: - set_dict[key] = value + # 处理来自实体的 + where_dict11 = {} + set_dict11 = {} # 是否有顺序,没有顺序,还要考虑是否符合要求 Python3.6后是有顺序的 + for key, value in entityFilter.items(): + if key in whereFields: + where_dict11[key] = value + else: + set_dict11[key] = value - if not set_dict: - raise SqlBeeException("UpdateBy SQL's set part is empty!") - - null_value_filter = set(whereFields) - set(where_dict) + null_value_filter = set(whereFields) - set(where_dict11) + updateSetStr_inCondtion = None + conditionUpdateSetStruct = None + where = None if condition: condition.suidType(SuidType.UPDATE) - conditionStruct=condition.parseCondition() - where=conditionStruct.where - if not where_dict and not where: + conditionStruct = condition.parseCondition() + where = conditionStruct.where + # 还要检测是否在condition设置有,要是有,就不用使用is null + null_value_filter = null_value_filter - conditionStruct.whereFields + if not where_dict11 and not where and not null_value_filter: Logger.warn("Update SQL's where part is empty, would update all records!") + + conditionUpdateSetStruct = condition.parseConditionUpdateSet() + updateSetStr_inCondtion = conditionUpdateSetStruct.updateSet - set_dict2 = self.__toColumns_with_dict(set_dict) - conditions2 = self.__toColumns_with_dict(where_dict) + if not set_dict11 and not updateSetStr_inCondtion: + raise SqlBeeException("UpdateBy SQL's set part is empty!") + else: + if not set_dict11: + raise SqlBeeException("UpdateBy SQL's set part is empty!") - ph=self.__getPlaceholder() + set_dict12 = self.__toColumns_with_dict(set_dict11) + where12 = self.__toColumns_with_dict(where_dict11) # 来自entityFilter + + condition_str = "" + updateSet = None + ph = self.__getPlaceholder() if self.__getPlaceholderType() == 3: - updateSet = ', '.join(f"{key} = {ph}{key}" for key in set_dict2.keys()) - condition_str = f" {K.and_()} ".join(f"{key} = {ph}{key}" for key in conditions2.keys()) + if set_dict12: + updateSet = ', '.join(f"{key} = {ph}{key}" for key in set_dict12.keys()) + if where12: + condition_str = f" {K.and_()} ".join(f"{key} = {ph}{key}" for key in where12.keys()) else: - updateSet = ', '.join(f"{key} = {ph}" for key in set_dict2.keys()) - condition_str = f" {K.and_()} ".join(f"{key} = {ph}" for key in conditions2.keys()) + if set_dict12: + updateSet = ', '.join(f"{key} = {ph}" for key in set_dict12.keys()) + if where12: + condition_str = f" {K.and_()} ".join(f"{key} = {ph}" for key in where12.keys()) + + # 增加来自condition的update set部分 + if updateSetStr_inCondtion: + if updateSet: + updateSet += " , " + updateSetStr_inCondtion + else: + updateSet = updateSetStr_inCondtion - if null_value_filter: + if null_value_filter: # null_value_filter transfer to where no_value_filter2 = self.__toColumns(null_value_filter) if condition_str: - condition_str+=" " +{K.and_()} + condition_str += " " + K.and_() + " " condition_str += f" {K.and_()} ".join(f"{key} {K.isnull()}" for key in no_value_filter2) if condition_str: sql = f"{K.update()} {table_name} {K.set()} {updateSet} {K.where()} {condition_str}" else: sql = f"{K.update()} {table_name} {K.set()} {updateSet}" - params = list(set_dict2.values()) + list(conditions2.values()) - - if where: - sql,params=self.__appendWhere(sql, params, whereFields, conditionStruct) + + params = [] + # 调整values: entityFilter's set-> conditon's set-> entityFilter's where-> conditon's where + if set_dict12: + params = list(set_dict12.values()) + # 是否能够保证where12.keys()与where12.values()顺序对应.会保证 + # params = params+conditionUpdateSetStruct.values + list(where12.values()) + if conditionUpdateSetStruct and conditionUpdateSetStruct.values: + params += conditionUpdateSetStruct.values + if where12: + params += list(where12.values()) + + if where: # conditon's where + sql, params = self.__appendWhere(sql, params, whereFields, conditionStruct) - return sql, params - + return sql, params, table_name def __build_select_by_id_sql(self, table_name, classField, where_condition_str): if not classField: @@ -402,10 +437,8 @@ class ObjToSQL: sql = f"{K.delete()} {K.from_()} {table_name}" return sql + where_condition_str - def __get_dbName(self): - honeyConfig=HoneyConfig() - return honeyConfig.get_dbName() + return HoneyConfig().get_dbname() def __getPlaceholderType(self): # sql = "SELECT * FROM employees WHERE employee_id = :emp_id" @@ -417,111 +450,90 @@ class ObjToSQL: else: return 0 - def __build_select_fun_sql(self, table_name, functionType, field_for_fun, conditions=None): - column_for_fun=NamingHandler.toColumnName(field_for_fun) + def __build_select_fun_sql(self, table_name, functionType, field_for_fun, conditions = None): + column_for_fun = NamingHandler.toColumnName(field_for_fun) # sql = f"SELECT count() FROM {table_name}" sql = f"{K.select()} {functionType.get_name()}({column_for_fun}) {K.from_()} {table_name}" - #where part + # where part params = [] if conditions: - condition_str=self.__build_where_condition(conditions) + condition_str = self.__build_where_condition(conditions) sql += f" {K.where()} {condition_str}" params = list(conditions.values()) - return sql, params + return sql, params, table_name - #ddl - def toCreateSQL(self, entityClass): - classField = HoneyUtil.get_class_field(entityClass) # list - # fieldAndValue, classField = self.__getKeyValue_classField(entity) - pk = HoneyUtil.get_pk_by_class(entityClass) - table_name = HoneyUtil.get_table_name_by_class(entityClass) - if pk is None: - if SysConst.id in classField: + # ddl + def toCreateSQL(self, cls): + """根据实体类生成创建表的 SQL 语句""" + + field_and_type = HoneyUtil.get_field_and_type(cls) + + # p1.主键 + pk = HoneyUtil.get_pk_by_class(cls) + table_name = HoneyUtil.get_table_name_by_class(cls) + if not pk: + if SysConst.id in field_and_type: pk = SysConst.id else: Logger.warn("There are no primary key when create table: " + table_name) - - # print(pk) # bug - # classField.remove(pk) - hasPk = False - if pk in classField: - classField.remove(pk) - hasPk = True - - classField = self.__toColumns(classField) - field_type = "VARCHAR(255)" # 假设所有非主键字段都是 VARCHAR(255) TODO + sql_fields = [] + # p1.5 创建主键字段语句 + if pk: + # pk_type = field_and_type.pop(pk, None) + pk_type = field_and_type[pk] + if pk_type and pk_type == str: + field_and_type[pk] = None + else: + pk = NamingHandler.toColumnName(pk) + pk_statement = pk + HoneyUtil.adjustUpperOrLower(HoneyUtil.generate_pk_statement()) + sql_fields.append(pk_statement) + field_and_type.pop(pk, None) + + for field_name, field_type in field_and_type.items(): + sql_type = HoneyUtil.python_type_to_sql_type(field_type) + column_name = NamingHandler.toColumnName(field_name) + sql_fields.append(f"{column_name} {sql_type}") + + sql_statement = f"CREATE TABLE {table_name} (\n " + ",\n ".join(sql_fields) + "\n);" + return sql_statement - pk_statement = "" - # 创建主键字段语句 - if hasPk: - pk = NamingHandler.toColumnName(pk) - pk_statement = self.generate_pk_statement(pk) - - # 创建其他字段语句 - fields_statement = [f"{field} {field_type}" for field in classField] - - if hasPk: - # 合并主键和字段 - all_fields_statement = [pk_statement] + fields_statement - else: - all_fields_statement = fields_statement - - # 生成完整的 CREATE TABLE 语句 - create_sql = f"CREATE TABLE {table_name} (\n " + ',\n '.join(all_fields_statement) + "\n);" - return create_sql - - def generate_pk_statement(self, pk): - honeyConfig = HoneyConfig() - dbName = honeyConfig.get_dbName() - if dbName == DatabaseConst.MYSQL.lower(): - return f"{pk} INT PRIMARY KEY AUTO_INCREMENT NOT NULL" - elif dbName == DatabaseConst.SQLite.lower(): - return f"{pk} INTEGER PRIMARY KEY" # 自动增长 - elif dbName == DatabaseConst.ORACLE.lower(): - return f"{pk} NUMBER PRIMARY KEY" - elif dbName == DatabaseConst.PostgreSQL.lower(): - return f"{pk} SERIAL PRIMARY KEY" - else: - raise ValueError(f"Unsupported database type: {dbName}") - def toDropTableSQL(self, entityClass): - honeyConfig = HoneyConfig() - table_name = HoneyUtil.get_table_name_by_class(entityClass) - dbName = honeyConfig.get_dbName() + if type(entityClass) == str: + table_name = entityClass + else: + table_name = HoneyUtil.get_table_name_by_class(entityClass) + dbName = HoneyConfig().get_dbname() if dbName == DatabaseConst.ORACLE.lower() or dbName == DatabaseConst.SQLSERVER.lower(): - sql0 = "DROP TABLE " + table_name; + sql0 = "DROP TABLE " + table_name else: - sql0 = "DROP TABLE IF EXISTS " + table_name; + sql0 = "DROP TABLE IF EXISTS " + table_name return sql0 def to_index_sql(self, entity_class, fields, index_name, prefix, index_type_tip, index_type): if not fields: raise ValueError(f"Create {index_type_tip} index, the fields can not be empty!") - NameCheckUtil.check_field(fields) + NameCheckUtil.check_fields(fields) table_name = HoneyUtil.get_table_name_by_class(entity_class) columns = self.transfer_field(fields, entity_class) if not index_name: index_name = f"{prefix}{table_name}_{columns.replace(',', '_')}" else: - NameCheckUtil.check_field(index_name) + NameCheckUtil.check_fields(index_name) index_sql = f"CREATE {index_type}INDEX {index_name} ON {table_name} ({columns})" return index_sql def transfer_field(self, fields, entity_class): - # 根据实际的实体类转换字段名 - # return fields # 这里简单返回,可以根据需求进行字段转换 TODO return NamingHandler.toColumnName(fields) def to_drop_index_sql(self, entity_class, index_name): table_name = HoneyUtil.get_table_name_by_class(entity_class) - honeyConfig = HoneyConfig() - dbName = honeyConfig.get_dbName() + dbName = HoneyConfig().get_dbname() if index_name: if dbName == DatabaseConst.SQLSERVER.lower(): diff --git a/src/bee/paging.py b/src/bee/osql/paging.py similarity index 75% rename from src/bee/paging.py rename to src/bee/osql/paging.py index 5e90680..cf41abf 100644 --- a/src/bee/paging.py +++ b/src/bee/osql/paging.py @@ -3,12 +3,14 @@ from bee.exception import SqlBeeException from bee.osql.const import DatabaseConst from bee.osql.sqlkeyword import K +from bee.custom import Custom + + class Paging: def to_page_sql(self, sql, start, size): - config = HoneyConfig() - dbName = config.get_dbName() - if dbName is None: + dbName = HoneyConfig().get_dbname() + if not dbName: raise SqlBeeException("dbName is None!") # return sql elif dbName == DatabaseConst.MYSQL.lower(): @@ -16,7 +18,7 @@ class Paging: elif dbName == DatabaseConst.SQLite.lower(): return self.__toLimitOffsetPaging(sql, start, size) else: - return self.custom_to_page_sql(sql, start, size) #TODO + return Custom.custom_to_page_sql(sql, start, size) # todo def __toPageSqlForMySql(self, sql, start, size): limitStament = " " + K.limit() + " " + str(start) + ", " + str(size) @@ -26,6 +28,3 @@ class Paging: def __toLimitOffsetPaging(self, sql, offset, size): return sql + " " + K.limit() + " " + str(size) + " " + K.offset() + " " + str(offset) - def custom_to_page_sql(self, sql, start, size): - raise NotImplementedError - diff --git a/src/bee/osql/sqlkeyword.py b/src/bee/osql/sqlkeyword.py index 60c3a8d..273940f 100644 --- a/src/bee/osql/sqlkeyword.py +++ b/src/bee/osql/sqlkeyword.py @@ -414,244 +414,248 @@ class LowerKey(SqlKeyWord): class K: - _sql_keywords = None + __sql_keywords = None @classmethod - def _get_sql_keywords(cls): - if HoneyUtil.is_sql_key_word_upper(): # 根据配置指定是用大写还是小写 - return UpperKey() - else: - return LowerKey() # 默认使用小写 + def _get_sql_keywords(cls): + try: + if HoneyUtil.is_sql_key_word_upper(): # 根据配置指定是用大写还是小写 + return UpperKey() + except Exception: + pass + + return LowerKey() # 默认使用小写 + @classmethod def _initialize(cls): - if cls._sql_keywords is None: - cls._sql_keywords = cls._get_sql_keywords() + if not cls.__sql_keywords: + cls.__sql_keywords = cls._get_sql_keywords() @classmethod def select(cls): cls._initialize() - return cls._sql_keywords.select() + return cls.__sql_keywords.select() @classmethod def as_(cls): cls._initialize() - return cls._sql_keywords.as_() + return cls.__sql_keywords.as_() @classmethod def from_(cls): cls._initialize() - return cls._sql_keywords.from_() + return cls.__sql_keywords.from_() @classmethod def where(cls): cls._initialize() - return cls._sql_keywords.where() + return cls.__sql_keywords.where() @classmethod def insert(cls): cls._initialize() - return cls._sql_keywords.insert() + return cls.__sql_keywords.insert() @classmethod def replace(cls): cls._initialize() - return cls._sql_keywords.replace() + return cls.__sql_keywords.replace() @classmethod def into(cls): cls._initialize() - return cls._sql_keywords.into() + return cls.__sql_keywords.into() @classmethod def values(cls): cls._initialize() - return cls._sql_keywords.values() + return cls.__sql_keywords.values() @classmethod def and_(cls): cls._initialize() - return cls._sql_keywords.and_() + return cls.__sql_keywords.and_() @classmethod def or_(cls): cls._initialize() - return cls._sql_keywords.or_() + return cls.__sql_keywords.or_() @classmethod def not_(cls): cls._initialize() - return cls._sql_keywords.not_() + return cls.__sql_keywords.not_() @classmethod def null(cls): cls._initialize() - return cls._sql_keywords.null() + return cls.__sql_keywords.null() @classmethod def isnull(cls): cls._initialize() - return cls._sql_keywords.isnull() + return cls.__sql_keywords.isnull() @classmethod def is_not_null(cls): cls._initialize() - return cls._sql_keywords.is_not_null() + return cls.__sql_keywords.is_not_null() @classmethod def update(cls): cls._initialize() - return cls._sql_keywords.update() + return cls.__sql_keywords.update() @classmethod def set(cls): cls._initialize() - return cls._sql_keywords.set() + return cls.__sql_keywords.set() @classmethod def delete(cls): cls._initialize() - return cls._sql_keywords.delete() + return cls.__sql_keywords.delete() @classmethod def order_by(cls): cls._initialize() - return cls._sql_keywords.order_by() + return cls.__sql_keywords.order_by() @classmethod def count(cls): cls._initialize() - return cls._sql_keywords.count() + return cls.__sql_keywords.count() @classmethod def asc(cls): cls._initialize() - return cls._sql_keywords.asc() + return cls.__sql_keywords.asc() @classmethod def on(cls): cls._initialize() - return cls._sql_keywords.on() + return cls.__sql_keywords.on() @classmethod def limit(cls): cls._initialize() - return cls._sql_keywords.limit() + return cls.__sql_keywords.limit() @classmethod def offset(cls): cls._initialize() - return cls._sql_keywords.offset() + return cls.__sql_keywords.offset() @classmethod def top(cls): cls._initialize() - return cls._sql_keywords.top() + return cls.__sql_keywords.top() @classmethod def group_by(cls): cls._initialize() - return cls._sql_keywords.group_by() + return cls.__sql_keywords.group_by() @classmethod def having(cls): cls._initialize() - return cls._sql_keywords.having() + return cls.__sql_keywords.having() @classmethod def between(cls): cls._initialize() - return cls._sql_keywords.between() + return cls.__sql_keywords.between() @classmethod def not_between(cls): cls._initialize() - return cls._sql_keywords.not_between() + return cls.__sql_keywords.not_between() @classmethod def for_update(cls): cls._initialize() - return cls._sql_keywords.for_update() + return cls.__sql_keywords.for_update() @classmethod def distinct(cls): cls._initialize() - return cls._sql_keywords.distinct() + return cls.__sql_keywords.distinct() @classmethod def join(cls): cls._initialize() - return cls._sql_keywords.join() + return cls.__sql_keywords.join() @classmethod def inner_join(cls): cls._initialize() - return cls._sql_keywords.inner_join() + return cls.__sql_keywords.inner_join() @classmethod def left_join(cls): cls._initialize() - return cls._sql_keywords.left_join() + return cls.__sql_keywords.left_join() @classmethod def right_join(cls): cls._initialize() - return cls._sql_keywords.right_join() + return cls.__sql_keywords.right_join() @classmethod def in_(cls): cls._initialize() - return cls._sql_keywords.in_() + return cls.__sql_keywords.in_() @classmethod def not_in(cls): cls._initialize() - return cls._sql_keywords.not_in() + return cls.__sql_keywords.not_in() @classmethod def exists(cls): cls._initialize() - return cls._sql_keywords.exists() + return cls.__sql_keywords.exists() @classmethod def not_exists(cls): cls._initialize() - return cls._sql_keywords.not_exists() + return cls.__sql_keywords.not_exists() @classmethod def union(cls): cls._initialize() - return cls._sql_keywords.union() + return cls.__sql_keywords.union() @classmethod def union_all(cls): cls._initialize() - return cls._sql_keywords.union_all() + return cls.__sql_keywords.union_all() @classmethod def truncate(cls): cls._initialize() - return cls._sql_keywords.truncate() + return cls.__sql_keywords.truncate() @classmethod def table(cls): cls._initialize() - return cls._sql_keywords.table() + return cls.__sql_keywords.table() @classmethod def drop(cls): cls._initialize() - return cls._sql_keywords.drop() + return cls.__sql_keywords.drop() @classmethod def if_(cls): cls._initialize() - return cls._sql_keywords.if_() + return cls.__sql_keywords.if_() @classmethod def to_date(cls): cls._initialize() - return cls._sql_keywords.to_date() + return cls.__sql_keywords.to_date() # 在类外部访问静态属性 diff --git a/src/bee/sqllib.py b/src/bee/osql/sqllib.py similarity index 57% rename from src/bee/sqllib.py rename to src/bee/osql/sqllib.py index 5bda526..62e67bc 100644 --- a/src/bee/sqllib.py +++ b/src/bee/osql/sqllib.py @@ -1,19 +1,29 @@ from bee.context import HoneyContext from bee.exception import SqlBeeException, BeeException from bee.osql.logger import Logger -from bee.util import HoneyUtil +from bee.osql.base import AbstractBase +from bee.osql.cache import CacheUtil +from bee.osql.transform import ParamUtil, ResultUtil -class BeeSql: - def select(self, sql, entityClass, params=None): - # def select(self, sql: str, entityClass: type, params=None) -> list: +class BeeSql(AbstractBase): + + def select(self, sql, entityClass, params = None): + # def select(self, sql: str, entityClass: type, params=None) -> list: + cacheObj = CacheUtil.get(sql) + if cacheObj is not None: + super().loginfo("---------------get from cache") + super().loginfo(" | <-- select rows: " + str(len(cacheObj))) + return cacheObj + conn = self.__getConn() rs_list = [] try: cursor = conn.cursor() - ## with conn.cursor() as cursor: # SQLite不支持with语法 + # # with conn.cursor() as cursor: # SQLite不支持with语法 + params = ParamUtil.transform_param(params) # 执行 SQL 查询 cursor.execute(sql, params or []) # 获取列名 @@ -23,27 +33,32 @@ class BeeSql: for row in results: # 将行数据映射到新创建的实体对象 - target_obj = HoneyUtil.transform_result(row, column_names, entityClass) + target_obj = ResultUtil.transform_result(row, column_names, entityClass) rs_list.append(target_obj) - Logger.info(" | <-- select rows: " + str(len(rs_list))) + super().loginfo(" | <-- select rows: " + str(len(rs_list))) + super().addInCache(sql, rs_list, len(rs_list)) except Exception as e: raise SqlBeeException(e) finally: self.__close(cursor, conn) return rs_list - """ 执行 UPDATE/INSERT/DELETE 操作 """ + # def modify(self, sql: str, params=None) -> int: - def modify(self, sql, params=None): + def modify(self, sql, params = None): conn = self.__getConn() a = 0 try: cursor = conn.cursor() + params = ParamUtil.transform_param(params) cursor.execute(sql, params or []) conn.commit() a = cursor.rowcount # 返回受影响的行数 - Logger.info(" | <-- Affected rows: " + str(a)) + super().loginfo(" | <-- Affected rows: " + str(a)) + + if a > 0: + CacheUtil.clear(sql) return a except Exception as e: Logger.error(f"Error in modify: {e}") @@ -52,15 +67,20 @@ class BeeSql: finally: self.__close(cursor, conn) - def batch(self, sql, params=None): + def batch(self, sql, params = None): conn = self.__getConn() a = 0 try: cursor = conn.cursor() + params = ParamUtil.transform_list_tuple_param(params) cursor.executemany(sql, params or []) conn.commit() a = cursor.rowcount # 返回受影响的行数 - Logger.info(" | <-- Affected rows: " + str(a)) + super().loginfo(" | <-- Affected rows: " + str(a)) + + if a > 0: + CacheUtil.clear(sql) + return a except Exception as e: Logger.error(f"Error in batch: {e}") @@ -69,24 +89,34 @@ class BeeSql: finally: self.__close(cursor, conn) - - def select_fun(self, sql, params=None): + def select_fun(self, sql, params = None): + + cacheObj = CacheUtil.get(sql) + if cacheObj is not None: + super().loginfo("---------------get from cache") + super().loginfo(" | <-- select rows: 1") + return cacheObj conn = self.__getConn() - rs_list = [] + rs_fun = '' try: cursor = conn.cursor() + params = ParamUtil.transform_param(params) cursor.execute(sql, params or []) result = cursor.fetchone() # 返回一个元组,例如 (1,) if result[0]: - Logger.info(" | <-- select rows: 1" ) + super().loginfo(" | <-- select rows: 1") + + super().addInCache(sql, result[0], 1) + return result[0] except Exception as e: raise SqlBeeException(e) finally: self.__close(cursor, conn) - return rs_list + + return rs_fun def __getConn(self): try: @@ -94,7 +124,7 @@ class BeeSql: except Exception as e: raise BeeException(e) - if conn is None: + if not conn: raise SqlBeeException("DB conn is None!") return conn diff --git a/src/bee/osql/struct.py b/src/bee/osql/struct.py new file mode 100644 index 0000000..5381d9d --- /dev/null +++ b/src/bee/osql/struct.py @@ -0,0 +1,38 @@ + + +class CacheSuidStruct: + sql:str # 不带值的 + tableNames:str + params = None + returnType:str # 返回值类型用于过滤缓存的查询结果,防止同一查询sql的不同类型的结果; 但更改的操作可不需要用这个值 + suidType:str # 操作类型 + entityClass = None + + def __init__(self, sql, params, tableNames, returnType, entityClass, suidType = None): + self.sql = sql + self.params = params + self.tableNames = tableNames + self.returnType = returnType + self.entityClass = entityClass + self.suidType = suidType + + def __str__(self): + return str(self) + + +class TableMeta: + col = None + type = None + ynNull:bool = None # 是否允许为空 + ynKey:bool = None # 是否是主键 + label = None # 标题,列名注释 + defaultValue = None + strLen:int = None + unique:bool = None + precision:int = None + scale:int = None + # tablecomment = None + # tablename = None + + def __repr__(self): + return str(self.__dict__) \ No newline at end of file diff --git a/src/bee/osql/transform.py b/src/bee/osql/transform.py new file mode 100644 index 0000000..3d4adbb --- /dev/null +++ b/src/bee/osql/transform.py @@ -0,0 +1,94 @@ +from datetime import time +import json +from typing import Dict, List, Tuple, Set + +from bee.context import HoneyContext +from bee.name.naming_handler import NamingHandler +from bee.osql.logger import Logger +from bee.util import HoneyUtil + + +class ResultUtil: + + """将结果集的一行转换为实体对象""" + + @staticmethod + def transform_result(row, column_names, entity_class): + + field_and_type = HoneyUtil.get_field_and_type(entity_class) # TODO put in cache + # 创建实体类的新实例 + obj = entity_class() + for i in range(len(column_names)): + fieldName = NamingHandler.toFieldName(column_names[i]) + # 获取字段的类型 + field_type = field_and_type[fieldName] + v = row[i] + if field_type is bool: + # v = bool(v) # 将值转换为bool类型 + # print(type(v)) + if type(v) == int: + v = bool(v) + else: + v = (v == '1') or (v.lower() == 'true') + elif field_type in (dict, list, Dict, List): + # v=dict(row[i]) + if v: + try: + v = json.loads(v) + except Exception as e: + Logger.warn("transform '" + v + "' to json have exception! " + str(e)) + elif field_type in (tuple, Tuple): + if v: + try: + v = tuple(json.loads(v)) + except Exception as e: + Logger.warn("transform '" + v + "' to json have exception! " + str(e)) + elif field_type in (set, Set): + # set不保证顺序和原来的一样 + if v: + try: + v = set(json.loads(v)) + except Exception as e: + Logger.warn("transform '" + v + "' to json have exception! " + str(e)) + else: + v = row[i] + setattr(obj, fieldName, v) + return obj + + +class ParamUtil: + + @staticmethod + def transform_param(params: list): + + if not params: + return params + + new_params = [] + for item in params: + # 这里不需要判断Dict,List等,因value会是实现的类型,List只是类型提示。 + if isinstance(item, dict) or isinstance(item, list) or isinstance(item, tuple): + # new_params.append(str(item)) + new_params.append(json.dumps(item)) + elif isinstance(item, set): + new_params.append(json.dumps(list(item))) + elif HoneyContext.isSQLite() and isinstance(item, time): + new_params.append(item.strftime('%H:%M:%S')) + else: + new_params.append(item) + return new_params + # return params + + @staticmethod + def transform_list_tuple_param(params): + if not params: + return params + + converted_params = [] + for item in params: + # 将 tuple 转换为 list,调用 transform_param 方法 + transformed_list = ParamUtil.transform_param(list(item)) + # 将处理后的 list 转换回 tuple + converted_params.append(tuple(transformed_list)) + return converted_params + diff --git a/src/bee/osql/type_transform.py b/src/bee/osql/type_transform.py new file mode 100644 index 0000000..87bfb1d --- /dev/null +++ b/src/bee/osql/type_transform.py @@ -0,0 +1,512 @@ +from datetime import date, time, datetime +import decimal +from typing import Dict, List, Set, Tuple + +from bee.context import HoneyContext +from bee.osql.const import DatabaseConst +from bee.typing import String + + +class Py2Sql: + __instance = None + __import_check = True + __ANNOTATED_SUPPORTED = False + + _type_mappings: Dict[str, Dict[str, str]] = {} + + def __new__(cls): + if cls.__instance is None: + cls.__instance = super().__new__(cls) + return cls.__instance + + def __init__(self): + # 初始化 _type_mappings(如果尚未初始化) + if not Py2Sql._type_mappings: + self._init_py_type() + + def python_type_to_sql_type(self, python_type): + return self.__python_type_to_sql_type0(python_type) + + def __python_type_to_sql_type0(self, python_type): + + if isinstance(python_type, String): + # print(">>>>>>>>>>>>>>>>>>>>>>",python_type.len) + # e.g. String(100) + return self.__default_type() + f"({python_type.len})" + + #check for Python whether support Annotated or version <=3.8.10 + if Py2Sql.__import_check or Py2Sql.__ANNOTATED_SUPPORTED: + try: + Py2Sql.__import_check = False + from typing import Annotated, get_origin, get_args + Py2Sql.__ANNOTATED_SUPPORTED = True + except ImportError: + Py2Sql.__import_check = False + Py2Sql.__ANNOTATED_SUPPORTED = False + if not hasattr(Py2Sql, '_error_printed'): + print("\033[31m[ERROR] Note: Python's version<=3.8.10 do not support Annotated,get_origin and get_args! \033[0m ") + Py2Sql._error_printed = True # 设置类变量,标记错误消息已打印 + + # 获取复合类型的基本类型。 + if Py2Sql.__ANNOTATED_SUPPORTED: + if get_origin(python_type) is Annotated: + base_type, *annotations = get_args(python_type) + if base_type is str: + # 提取字符串长度注解 + for annotation in annotations: + if isinstance(annotation, str): + annotation = annotation.replace(" ", "") + if (annotation.startswith("length=") or annotation.startswith("len=")): + length = int(annotation.split("=")[1]) + return self.__default_type() + f"({length})" + return self.python_type_to_sql_type(base_type) + + type_mapping = Py2Sql._type_mappings.get(HoneyContext.get_dbname(), Py2Sql._type_mappings["COMMON"]) + + return type_mapping.get(python_type, self.__default_type() + "(255)") + + def __default_type(self): + if HoneyContext.isOracle(): + return "VARCHAR2" + else: + return "VARCHAR" # 默认使用 VARCHAR + + def _init_py_type(self): + + common_type_mappings: Dict[type, str] = { + str: "VARCHAR(255)", + + set: "TEXT", + dict: "JSON", + list: "TEXT", + tuple: "TEXT", + + Set: "TEXT", + Dict: "JSON", + List: "TEXT", + Tuple: "TEXT", + + decimal.Decimal:"Numeric", + + # 如果需要,可以加入支持的类型 + String: "VARCHAR(255)", + + type(None): "VARCHAR(255)", + } + + Py2Sql._type_mappings: Dict[str, Dict[type, str]] = { + + DatabaseConst.MYSQL.lower(): { + **common_type_mappings, # 引用公共类型映射 + int: "INT(11)", + float: "FLOAT(19,6)", + bool: "TINYINT(1)", + + date: "DATETIME", + time: "TIME", + datetime: "DATETIME", + + bytes:"BIT(64)" + }, + DatabaseConst.MariaDB.lower(): { + **common_type_mappings, # 引用公共类型映射 + int: "INT(11)", + float: "FLOAT(19,6)", + bool: "TINYINT(1)", + + date: "DATETIME", + time: "TIME", + datetime: "DATETIME", + + bytes:"BIT(64)" + }, + DatabaseConst.ORACLE.lower(): { + **common_type_mappings, # 引用公共类型映射 + int: "NUMBER(10)", + float: "NUMBER(19,6)", + bool: "VARCHAR2(1)", + + date: "DATE", + time: "DATE", + datetime: "DATE", + + str: "VARCHAR2(255)", + type(None): "VARCHAR2(255)", + }, + DatabaseConst.PostgreSQL.lower(): { + **common_type_mappings, # 引用公共类型映射 + int: "INT4", + float: "FLOAT4", + bool: "BIT", + + date: "DATE", + time: "TIME", # Adjust according to PostgreSQL's time type + datetime: "TIMESTAMP", # Or "TIMESTAMPTZ" for timezone-aware + }, + DatabaseConst.SQLite.lower():{ + **common_type_mappings, # 引用公共类型映射 + int: "int(11)", + float: "FLOAT4", + bool: "BOOLEAN", + + date: "DATETIME", # 日期字段 + time: "VARCHAR(8)", + datetime: "DATETIME", # 日期时间字段 + }, + DatabaseConst.H2.lower():{ + **common_type_mappings, # 引用公共类型映射 + int: "INT4", + float: "FLOAT4", + bool: "BIT", + + date: "DATETIME", # 日期字段 + time: "VARCHAR(8)", + datetime: "DATETIME", # 日期时间字段 + + str: "VARCHAR2(255)", + type(None): "VARCHAR2(255)", + }, + DatabaseConst.SQLSERVER.lower():{ + **common_type_mappings, # 引用公共类型映射 + int: "int", + float: "real", + bool: "char(1)", + + date: "datetime", # 日期字段 + time: "time", + datetime: "datetime", # 日期时间字段 + + str: "nvarchar(255)", + type(None): "nvarchar(255)", + }, + "COMMON":{ + **common_type_mappings, # 引用公共类型映射 + int: "int(11)", + float: "FLOAT", + bool: "BOOLEAN", + + date: "DATE", # 日期字段 + time: "VARCHAR(8)", # 日期字段 #todo + # time: "TIME", # 日期字段 + datetime: "DATETIME", # 日期时间字段 + }, + + } + + """将 Python 类型映射到 SQL 类型,支持字符串长度和时间字段""" + + +class Sql2Py: + __instance = None + + _sql_type_mappings: Dict[str, Dict[str, str]] = {} + # common_sql_type_mappings: Dict[str, str] = {} + + def __new__(cls): + if cls.__instance is None: + cls.__instance = super().__new__(cls) + return cls.__instance + + def __init__(self): + # 初始化 _db_sql(如果尚未初始化) + if not Sql2Py._sql_type_mappings: + self._init_sql_type() + + def sql_type_to_python_type(self, sql_type): + type_mapping = Sql2Py._sql_type_mappings.get(HoneyContext.get_dbname(), Sql2Py._sql_type_mappings["COMMON"]) + + py_type = type_mapping.get(sql_type.lower(), type_mapping.get(sql_type.upper())) + if py_type is None: + py_type = "str" + return py_type + + def _init_sql_type(self): + + common_sql_type_mappings: Dict[str, str] = { + "VARCHAR":"str", + "TEXT":"str", + "JSON":"str", + "VARCHAR":"str", + "VARCHAR(255)":"str", + "int":"int", + "INT":"int", + "BIGINT":"int", + + "INTEGER":"int", + "BOOLEAN":"bool", + "BLOB":"bytes", + "REAL":"float", + + # None: "str", + } + + Sql2Py._sql_type_mappings: Dict[str, Dict[str, str]] = { + + DatabaseConst.MYSQL.lower(): { + **common_sql_type_mappings, # 引用公共类型映射 + "TINYINT":"bool", + "DATETIME":"date", + "TIME":"time", + "DATETIME":"datetime", + "BIT":"bytes", # 要判断长度 ? + "FLOAT":"float", + }, + DatabaseConst.MariaDB.lower(): { + **common_sql_type_mappings, # 引用公共类型映射 + "TINYINT":"bool", + "DATETIME":"date", + "TIME":"time", + "DATETIME":"datetime", + "BIT":"bytes", # 要判断长度 ? + "FLOAT":"float", + }, + DatabaseConst.ORACLE.lower(): { + + **common_sql_type_mappings, # 引用公共类型映射 + "NUMBER(10)":"int", + "NUMBER(19,6)":"float", # 要使用长度? TODO + "NUMBER":"int", + "VARCHAR2(1)":"bool", + "VARCHAR2":"str", + "DATE":"datetime", + }, + DatabaseConst.PostgreSQL.lower(): { + **common_sql_type_mappings, # 引用公共类型映射 + "FLOAT4":"float", + "INT4":"int", + "BIT":"bool", + + "DATE":"date", + "TIME":"time", + "TIMESTAMP":"datetime", + }, + DatabaseConst.SQLite.lower():{ + **common_sql_type_mappings, # 引用公共类型映射 + "FLOAT4":"float", + "INT4":"int", + "int(11)":"int", + "BOOLEAN":"bool", + "DATETIME":"datetime", + }, + DatabaseConst.H2.lower():{ + **common_sql_type_mappings, # 引用公共类型映射 + "VARCHAR2":"str", + "DATETIME":"datetime", + "BIT":"bool", + "FLOAT4":"float", + "INT4":"int", + }, + DatabaseConst.SQLSERVER.lower():{ + **common_sql_type_mappings, # 引用公共类型映射 + "real":"float", + "nvarchar":"str", + "char(1)":"bool", + "char":"str", + "datetime":"datetime", + "time":"time", + }, + "COMMON":{ + **common_sql_type_mappings, # 引用公共类型映射 + "INTEGER":"int", + "FLOAT":"float", + "BOOLEAN":"bool", + "DATE":"date", + "DATETIME":"datetime", + }, + } + + +class Mid: + __instance = None + + _mid_to_py_mappings: Dict[str, type] = {} + _mid_to_sqltype_mappings: Dict[str, Dict[str, str]] = {} + + def __new__(cls): + if cls.__instance is None: + cls.__instance = super().__new__(cls) + return cls.__instance + + def __init__(self): + # 初始化 _db_sql(如果尚未初始化) + if not Mid._mid_to_py_mappings: + self._init_type() + if not Mid._mid_to_sqltype_mappings: + self._init_mid_to_sqltype() + + def _init_type(self): + + #2 mid -> py type + Mid._mid_to_py_mappings: Dict[str, str] = { + "String":str, + "Text":str, + "JSON":str, + "VARCHAR":str, + "Integer":int, + "BigInteger":int, + # "SmallInt":int, + "SmallInteger":int, + "DateTime":datetime, + "Date":date, + "Time":time, + + "Float":float, + "Numeric":decimal.Decimal, + "DECIMAL":decimal.Decimal, + + "Boolean":bool, + "REAL":float, + + None: str, + } + + def mid_to_python_type(self, mid_type): + return Mid._mid_to_py_mappings.get(mid_type, str) + + def mid_to_sqltype(self, mid_type): + + type_mapping = Mid._mid_to_sqltype_mappings.get(HoneyContext.get_dbname(), Mid._mid_to_sqltype_mappings["COMMON"]) + + sql_type = type_mapping.get(mid_type.lower(), type_mapping.get(mid_type.upper(), None)) + return sql_type + + #1 mid->sql type mid到SQL直接映射 + def _init_mid_to_sqltype(self): + + common_type_mappings: Dict[str, str] = { + "string":"varchar", + "text":"varchar", + "json":"varchar", + "varchar":"varchar", + + "integer":"int", + # "smallint":"smallint", + "smallinteger":"smallint", + "biginteger":"bigint", + + "datetime":"DateTime", + "date":"Date", + "time":"Time", + + "float":"float", + "numeric":"Numeric", + + "boolean":"boolean", + "real":"float", + } + Mid._mid_to_sqltype_mappings: Dict[str, Dict[str, str]] = { + + DatabaseConst.MYSQL.lower(): { + **common_type_mappings, # 引用公共类型映射 + "int": "INT(11)", + "float": "FLOAT(19,6)", + "boolean": "TINYINT(1)", + "JSON":"JSON", + + "date": "DATETIME", + "time": "TIME", + "datetime": "DATETIME", + }, + DatabaseConst.MariaDB.lower(): { + **common_type_mappings, # 引用公共类型映射 + "int": "INT(11)", + "float": "FLOAT(19,6)", + "boolean": "TINYINT(1)", + + "date": "DATETIME", + "time": "TIME", + "datetime": "DATETIME", + }, + DatabaseConst.ORACLE.lower(): { + **common_type_mappings, # 引用公共类型映射 + + "string":"VARCHAR2", + "text":"VARCHAR2", + "JSON":"VARCHAR2", + "VARCHAR":"VARCHAR2", + + "biginteger":"NUMBER(19)", + "integer":"NUMBER(10)", + "smallint":"number(5)", + "smallinteger":"number(5)", + "datetime":"DATE", + "date":"DATE", + "time":"DATE", + + "float":"NUMBER(19,6)", + "numeric":"NUMBER", + + "boolean":"VARCHAR2(1)", + "real":"float", + }, + DatabaseConst.PostgreSQL.lower(): { + **common_type_mappings, # 引用公共类型映射 + "int": "INT4", + "float": "FLOAT4", + "boolean": "BIT", + + "date": "DATE", + "time": "TIME", # Adjust according to PostgreSQL's time type + "datetime": "TIMESTAMP", # Or "TIMESTAMPTZ" for timezone-aware + }, + DatabaseConst.SQLite.lower():{ + **common_type_mappings, # 引用公共类型映射 + "int": "int(11)", + "float": "FLOAT4", + + "date": "DATETIME", # 日期字段 + "time": "VARCHAR(8)", + "datetime": "DATETIME", # 日期时间字段 + }, + DatabaseConst.H2.lower():{ + **common_type_mappings, # 引用公共类型映射 + "int": "INT4", + "float": "FLOAT4", + "boolean": "BIT", + + "date": "DATETIME", # 日期字段 + "time": "VARCHAR(8)", + "datetime": "DATETIME", # 日期时间字段 + + "string": "VARCHAR2", + }, + DatabaseConst.SQLSERVER.lower():{ + **common_type_mappings, # 引用公共类型映射 + "int": "int", + "float": "real", + "boolean": "char(1)", + + "date": "datetime", # 日期字段 + "time": "time", + "datetime":"datetime", # 日期时间字段 + + "string": "nvarchar", + }, + "COMMON":{ + **common_type_mappings, # 引用公共类型映射 + "time": "VARCHAR(8)", # 日期字段 #todo + }, + } + +#sql/sqltypes.py +# Integer: 整数类型。 +# SMALLINT +# SmallInteger: 小整数类型,范围较小。 +# BigInteger: 大整数类型,范围更大。 +# Float: 浮点数类型。 +# Numeric: 精确的小数类型,支持定制精度和标度。 +# String(length): 字符串类型,可以指定长度。 +# Text: 大文本类型,不限制长度。 +# Boolean: 布尔类型,True 或 False。 +# Date: 日期类型,不包含时间。 +# Time: 时间类型,不包含日期。 +# DateTime: 日期和时间类型。 +# Interval: 时间间隔类型。 +# PickleType: 可序列化的 Python 对象类型。 +# LargeBinary: 二进制数据,适合存储大块数据。 +# +# JSON: JSON 数据类型,适用于存储 JSON 格式的数据。 +# ARRAY: 数组类型,适用于 PostgreSQL 等支持数组的数据库。 +# Enum: 枚举类型,限制到特定的字符串值集合。 +# ForeignKey: 外键类型,用于定义模型之间的关联。 + diff --git a/src/bee/typing.py b/src/bee/typing.py new file mode 100644 index 0000000..975f689 --- /dev/null +++ b/src/bee/typing.py @@ -0,0 +1,4 @@ +class String: + + def __init__(self, length): + self.len = length diff --git a/src/bee/util.py b/src/bee/util.py index 60f94c8..0fd6732 100644 --- a/src/bee/util.py +++ b/src/bee/util.py @@ -1,13 +1,17 @@ -from bee.config import PreConfig -from bee.osql.const import SysConst - +from bee.config import HoneyConfig from bee.name.naming_handler import NamingHandler +from bee.osql.const import SysConst, DatabaseConst +from bee.osql.logger import Logger +from bee.osql.type_transform import Py2Sql, Sql2Py, Mid + +from bee.custom import Custom +from bee.osql.mid_typing import Column -# from bee.key import Key class HoneyUtil: """返回给定对象的属性字典,如果没有则返回None""" + @staticmethod def get_obj_field_value(obj): if hasattr(obj, '__dict__'): @@ -22,19 +26,20 @@ class HoneyUtil: if hasattr(cls, '__dict__'): # 并去掉前缀__和_ 只是__开前开头,会变化的。 class_name = cls.__name__ - prefix="_"+class_name+"__" + prefix = "_" + class_name + "__" kv = { key[len(prefix):] if key.startswith(prefix) else key[1:] if key.startswith('_') else key: value for key, value in cls.__dict__.items() if not (key.startswith('__') and key.endswith('__'))} for key, value in kv.items(): if isinstance(value, property): - kv[key]=None #使用get/set,暂时不能获取到bean的类级别的值。 + kv[key] = None # 使用get/set,暂时不能获取到bean的类级别的值。 # kv[key]=getattr(cls, key) + elif isinstance(value, Column): + #print(value) + kv[key] = None return kv else: return None - - # if hasattr(cls, '__dict__'): # print(cls.__dict__) # return {key: value for key, value in cls.__dict__.items() if (not (key.startswith('__') and key.endswith('__'))) and not isinstance(value, property) } @@ -64,32 +69,22 @@ class HoneyUtil: # return result # dict: {'id': , 'name': , 'remark': } - """ 返回给定类的属性列表,但不包括系统的 """ + """ + 返回给定类的属性列表,但不包括系统的 + since 1.6.0 还考虑字段的类型;时间类型等 + """ + @staticmethod def get_class_field(cls): - if hasattr(cls, '__dict__'): - # 过滤掉以__开头和结尾的键,并去掉前缀__和_ 只是__开前开头,会变化的。 - class_name = cls.__name__ - prefix="_"+class_name+"__" - return [ - key[len(prefix):] if key.startswith(prefix) else key[1:] if key.startswith('_') else key - for key in cls.__dict__.keys() - if not (key.startswith('__') and key.endswith('__')) - ] - else: - return None - - # if hasattr(cls, '__dict__'): - # # 排除__开头且__结尾的 - # return [key for key in cls.__dict__.keys() if not (key.startswith('__') and key.endswith('__'))] - # else: - # return None + fieldname_and_type_dict = HoneyUtil.get_field_and_type(cls) + return fieldname_and_type_dict.keys() # 对象的不会改 """ remove __ or _ prefix """ + @staticmethod def remove_prefix(dict_obj): - if dict_obj is None: + if not dict_obj: return dict_obj fieldAndValue = { @@ -107,18 +102,19 @@ class HoneyUtil: # (None, 'David', 28, None, None), # remark 和 addr 均为空 # ] """ + @staticmethod def get_list_params(classField, entity_list): - dict_n = {i:None for i in classField} - # dict_classField=dict_n.copy() //TODO + dict_n = {e: None for e in classField} + # dict_classField=dict_n.copy() - list_params=[] - for entity in entity_list: + list_params = [] + for entity in entity_list: obj_dict = HoneyUtil.get_obj_field_value(entity) - dict_classField=dict_n.copy() + dict_classField = dict_n.copy() for k, v in obj_dict.items(): if v is not None and k in dict_classField: - dict_classField[k]=v + dict_classField[k] = v list_params.append(tuple(dict_classField.values())) return list_params @@ -135,8 +131,6 @@ class HoneyUtil: if temp_name and not temp_name.isspace(): return temp_name class_name = cls.__name__ - # table_name = class_name.lower() # 还要应用多种转换规则 TODO - # return table_name return NamingHandler.toTableName(class_name) """ get pk from bean""" @@ -158,23 +152,170 @@ class HoneyUtil: else: if hasattr(cls, SysConst.id): return SysConst.id - return None - - """将结果集的一行转换为实体对象""" - @staticmethod - def transform_result(row, column_names, entity_class): - - # 创建实体类的新实例 - obj = entity_class() - for i in range(len(column_names)): - setattr(obj, column_names[i], row[i]) # 像时间等类型,是否也可以自动设置??? TODO - return obj + return None @staticmethod def is_sql_key_word_upper(): - # TODO support set in config file - if PreConfig.sql_key_word_case: - if PreConfig.sql_key_word_case == SysConst.upper: + if HoneyConfig.sql_key_word_case: + if HoneyConfig.sql_key_word_case == SysConst.upper: return True return False + + @staticmethod + def generate_pk_statement(): + dbName = HoneyConfig().get_dbname() + if dbName == DatabaseConst.MYSQL.lower(): + return " INT PRIMARY KEY AUTO_INCREMENT NOT NULL" + elif dbName == DatabaseConst.SQLite.lower(): + return " INTEGER PRIMARY KEY NOT NULL" # 自动增长 + elif dbName == DatabaseConst.ORACLE.lower(): + return " NUMBER PRIMARY KEY" + elif dbName == DatabaseConst.PostgreSQL.lower(): + return " SERIAL PRIMARY KEY" + else: + # Logger.warn(f"Unsupported database type: {dbName}, when generate primary key!") + Logger.warn(f"There is not dedicated primary key statement for: {dbName}, will use normal!") + temp = " " + Custom.custom_pk_statement() + # if column: + # temp += ",\n PRIMARY KEY(" + column + ")" + return temp + # raise ValueError(f"Unsupported database type: {dbName}") + + @staticmethod + def adjustUpperOrLower(value): + + isUpper = HoneyUtil.is_sql_key_word_upper() + if isUpper: + return value.upper() + else: + return value.lower() + + @staticmethod + def python_type_to_sql_type(python_type): + type0 = Py2Sql().python_type_to_sql_type(python_type) + return HoneyUtil.adjustUpperOrLower(type0) + + @staticmethod + def sql_type_to_python_type(sql_type): + return Sql2Py().sql_type_to_python_type(sql_type) + + @staticmethod + def mid_type_to_sql_type(mid_type): + mid = Mid() + #直接找到,则直接返回 + sql_type = mid.mid_to_sqltype(mid_type) + if sql_type: + return sql_type + + python_type = mid.mid_to_python_type(mid_type) + # print('aaa:',python_type) + + type0 = Py2Sql().python_type_to_sql_type(python_type) + return HoneyUtil.adjustUpperOrLower(type0) + + @staticmethod + def get_class_normal_field(cls): + if hasattr(cls, '__dict__'): + # 过滤掉以__开头和结尾的键,并去掉前缀__和_ 只是__开前开头,会变化的。 + class_name = cls.__name__ + prefix = "_" + class_name + "__" + return [ + key[len(prefix):] if key.startswith(prefix) else key[1:] if key.startswith('_') else key + for key in cls.__dict__.keys() + if not (key.startswith('__') and key.endswith('__')) + ] + else: + return None + + @staticmethod + def get_field_and_type(cls): + # 声明基本类型和无声明类型的字段(保留定义顺序) + A = HoneyUtil.get_class_normal_field(cls) + + B = {} + try: + # 保留有类型的;包括复合类型; 低版本没有使用时,会报异常 + #3.8.10 have exception if no use like: remark: str = None + B = cls.__annotations__ + except Exception: + pass + + M = HoneyUtil.get_mid_field_and_type(cls) + # print(M) + if M: + if not B: + B = M + else: + B.update(M) + + # 保留有类型的;包括复合类型 + # B = cls.__annotations__ #3.8.10 have exception if no use like: remark: str = None + new_map = {} + + # none_type_set=set(A)-set(B) + # 复合类型 complex_type_set + ext = set(B) - set(A) + + if not ext: + for f in A: + if f in B: + # a1.声明类型的 + new_map[f] = B[f] + else: + # a2.无类型声明 + new_map[f] = None + else: + # A=A + # B=B + # ext=complex_type_set + B_keys = list(B.keys()) + # 使用简单的索引方式遍历 A 和 B + i, j = 0, 0 + len_A, len_B = len(A), len(B_keys) + + # 可以保证基本类型与无类型混合时的原始顺序 + while i < len_A and j < len_B: + # b1.声明类型的 + if A[i] == B_keys[j]: + new_map[B_keys[j]] = B[B_keys[j]] + i += 1 + j += 1 + # b2.复合类型 + elif B_keys[j] in ext: + new_map[B_keys[j]] = B[B_keys[j]] + j += 1 + # b3.无类型声明 + else: + new_map[A[i]] = None + i += 1 + + while i < len_A: + new_map[A[i]] = None + i += 1 + + while j < len_B: + new_map[B_keys[j]] = B[B_keys[j]] + j += 1 + + # 无声明类型与复合类型邻近,则无法识别;默认先处理复合类型(b2->b3) + # descstr=None + # modify_date: date + # updated_at2: date + # 结果变为: + # modify_date DATE, + # updated_at2 DATE, + # descstr VARCHAR(255) + + return new_map + + @staticmethod + def get_mid_field_and_type(cls): + M = {} + for name, obj in cls.__dict__.items(): + if isinstance(obj, Column): # 确认是Column对象 + field_type = obj.type + # print(f"字段名: {name}, 类型: {field_type}") + M[name] = Mid().mid_to_python_type(str(field_type)) + return M + diff --git a/src/bee/version.py b/src/bee/version.py index 8a35624..3841fa3 100644 --- a/src/bee/version.py +++ b/src/bee/version.py @@ -3,9 +3,8 @@ from bee.osql.const import StrConst class Version: __version = "1.6.0" - vid=1006000 + vid = 1006000 @staticmethod def printversion(): print("[INFO] ", StrConst.LOG_PREFIX, "Bee Version is: " + Version.__version) - -- Gitee From 8c84e5a50575b091084c8c982cee8dd29e414e4e Mon Sep 17 00:00:00 2001 From: aiteasoft Date: Sun, 20 Apr 2025 03:00:43 +0800 Subject: [PATCH 2/2] add/update test or exam for V1.6.0, condition,cache, and so on --- tests/MyConfig.py | 12 +++- tests/entity/Orders.py | 2 +- tests/entity/Student3.py | 9 +++ tests/entity/full.py | 52 ++++++++++++++ tests/entity/naming.py | 51 ++++++++++++++ tests/exam/BatchInsertTest.py | 31 +++++---- tests/exam/BatchInsertTest3.py | 2 +- tests/exam/BatchInsertTest4.py | 51 ++++++++++++++ tests/exam/SuidTest11Page.py | 4 +- tests/exam/SuidTest12_insert.py | 4 +- tests/exam/SuidTest13_insert.py | 2 +- tests/exam15/BatchInsertTest2.py | 24 ++++--- tests/exam15/DdlTest2.py | 4 -- tests/exam15/DdlTest5.py | 4 -- tests/exam15/PreTest3.py | 5 +- tests/exam15/PreTest4_test_placeholder.py | 45 ++++++++++++ tests/exam15/SelectFun.py | 7 +- tests/exam15/SuidTest1.py | 2 +- tests/exam16/Create.py | 28 ++++++++ tests/exam16/SuidTest1.py | 4 +- tests/exam16/SuidTest2.py | 10 +-- tests/exam16/SuidTest3.py | 19 +++++ tests/exam16/SuidTest4.py | 33 +++++++++ tests/exam16/SuidTest5.py | 33 +++++++++ tests/exam16/bugTest.py | 31 +++++++++ tests/exam16/cacheTest.py | 47 +++++++++++++ tests/exam16/cacheTest2.py | 45 ++++++++++++ tests/exam16/cacheTest3.py | 64 +++++++++++++++++ tests/exam16/cacheTest4.py | 85 +++++++++++++++++++++++ tests/exam16/conditionTest.py | 18 ++--- tests/exam16/conditionTest2.py | 12 +--- tests/exam16/conditionTest3_paging.py | 13 ++-- tests/exam16/conditionTest4_delete.py | 12 ++-- tests/exam16/conditionTest5_update.py | 7 +- tests/exam16/conditionTest5_update1.py | 45 ++++++++++++ tests/exam16/conditionTest5_update2.py | 46 ++++++++++++ tests/exam16/conditionTest5_update3.py | 47 +++++++++++++ tests/exam16/conditionTest5_update4.py | 41 +++++++++++ tests/exam16/conditionTest5_update4_3.py | 43 ++++++++++++ tests/exam16/conditionTest5_update5.py | 38 ++++++++++ tests/exam16/conditionTest5_update6.py | 41 +++++++++++ tests/exam16/conditionTest5_update7.py | 43 ++++++++++++ tests/exam16/conditionTest5_update8.py | 44 ++++++++++++ tests/exam16/config_test.py | 14 ++++ tests/exam16/full_type.py | 78 +++++++++++++++++++++ tests/exam16/full_type2.py | 66 ++++++++++++++++++ tests/exam16/full_type3.py | 63 +++++++++++++++++ tests/exam16/full_type4.py | 78 +++++++++++++++++++++ tests/exam16/genBeanTest.py | 26 +++++++ tests/exam16/select_someFields_paging.py | 34 +++++++++ tests/resources/bee.json | 2 +- tests/resources/bee.properties | 47 ++++++++----- 52 files changed, 1463 insertions(+), 105 deletions(-) create mode 100644 tests/entity/Student3.py create mode 100644 tests/entity/full.py create mode 100644 tests/entity/naming.py create mode 100644 tests/exam/BatchInsertTest4.py create mode 100644 tests/exam15/PreTest4_test_placeholder.py create mode 100644 tests/exam16/Create.py create mode 100644 tests/exam16/SuidTest3.py create mode 100644 tests/exam16/SuidTest4.py create mode 100644 tests/exam16/SuidTest5.py create mode 100644 tests/exam16/bugTest.py create mode 100644 tests/exam16/cacheTest.py create mode 100644 tests/exam16/cacheTest2.py create mode 100644 tests/exam16/cacheTest3.py create mode 100644 tests/exam16/cacheTest4.py create mode 100644 tests/exam16/conditionTest5_update1.py create mode 100644 tests/exam16/conditionTest5_update2.py create mode 100644 tests/exam16/conditionTest5_update3.py create mode 100644 tests/exam16/conditionTest5_update4.py create mode 100644 tests/exam16/conditionTest5_update4_3.py create mode 100644 tests/exam16/conditionTest5_update5.py create mode 100644 tests/exam16/conditionTest5_update6.py create mode 100644 tests/exam16/conditionTest5_update7.py create mode 100644 tests/exam16/conditionTest5_update8.py create mode 100644 tests/exam16/config_test.py create mode 100644 tests/exam16/full_type.py create mode 100644 tests/exam16/full_type2.py create mode 100644 tests/exam16/full_type3.py create mode 100644 tests/exam16/full_type4.py create mode 100644 tests/exam16/genBeanTest.py create mode 100644 tests/exam16/select_someFields_paging.py diff --git a/tests/MyConfig.py b/tests/MyConfig.py index e37aef4..84c6c74 100644 --- a/tests/MyConfig.py +++ b/tests/MyConfig.py @@ -1,6 +1,14 @@ -from bee.config import PreConfig +# import sys +from bee.config import PreConfig, HoneyConfig def init(): + + # python_version = sys.version # 获取当前 Python 版本 + # py_version=f'Current Python version: {python_version}' + # print(py_version) + #TODO change to your config file path - PreConfig.config_path="E:\\xxx\\project-name\\tests\\resources" \ No newline at end of file + PreConfig.config_path="E:\\xxx\\BeePy\\tests\\resources" + + HoneyConfig() # how to call first time \ No newline at end of file diff --git a/tests/entity/Orders.py b/tests/entity/Orders.py index f42eba3..c2bfc66 100644 --- a/tests/entity/Orders.py +++ b/tests/entity/Orders.py @@ -15,7 +15,7 @@ class Orders: remark = None # __pk__="id" #aaa - # __pk__="name" #aaa + # __pk__ = "name" # aaa # __primary_key__="name" #aaa #can ignore diff --git a/tests/entity/Student3.py b/tests/entity/Student3.py new file mode 100644 index 0000000..c090736 --- /dev/null +++ b/tests/entity/Student3.py @@ -0,0 +1,9 @@ +class Student3: + id:int = None + name = None + age:int = None + remark = None + addr = None + + def __repr__(self): + return str(self.__dict__) diff --git a/tests/entity/full.py b/tests/entity/full.py new file mode 100644 index 0000000..07655c2 --- /dev/null +++ b/tests/entity/full.py @@ -0,0 +1,52 @@ +from datetime import time, date, datetime +# from typing import Annotated, List, Set, Dict, Tuple +from typing import List, Set, Dict, Tuple + +from bee.typing import String + + +# full.py +class Entity: + id:int = None + name:str = None + # name2: Annotated[str, "length = 100"] # 声明字符串长度为 100 + # name3: Annotated[str, "len = 100"] # 声明字符串长度为 100 length or len + name4:String(99) + name5:String + price: float = None + created_at: datetime # 日期时间字段 + updated_time: time # 日期字段 + flag:bool = None + set0:set = None + map:dict = None + list0:list = None + list1:List = None + remark = None + tuple0:tuple = None + descstr:str = None + modify_date: date + updated_at2: date + + set1:Set = None + map1:Dict = None + tuple1:Tuple = None + + ttt:bytes=None + + # n:Number= None + # id3:Int = None + # name3:String = None + # flag3:BOOLEAN = None + + # 无声明类型与复合类型邻近,则无法识别;默认先处理复合类型 + # descstr=None + # modify_date: date + # updated_at2: date + # 结果变为: + # modify_date DATE, + # updated_at2 DATE, + # descstr VARCHAR(255) + + def __repr__(self): + return str(self.__dict__) + diff --git a/tests/entity/naming.py b/tests/entity/naming.py new file mode 100644 index 0000000..f58b621 --- /dev/null +++ b/tests/entity/naming.py @@ -0,0 +1,51 @@ +from datetime import time, date, datetime +from typing import Annotated, List, Set, Dict, Tuple + + +# full.py +class TestName: + id:int = None + myName:str = None + name2: Annotated[str, "length=100"] # 声明字符串长度为 100 + myPrice: float = None + createdAt: datetime # 日期时间字段 + updatedTime: time # 日期字段 + flag:bool = None + set0:set = None + map:dict = None + list0:list = None + list1:List = None + remark = None + tuple0:tuple = None + descstr:str = None + modifyDate: date + updatedAt2: date + + set1:Set = None + map1:Dict = None + tuple1:Tuple = None + + setTwo:Set = None + mapTwo:Dict = None + tupleTwo:Tuple = None + listTwo:List = None + + ttt:bytes=None + + # n:Number= None + # id3:Int = None + # name3:String = None + # flag3:BOOLEAN = None + + # 无声明类型与复合类型邻近,则无法识别;默认先处理复合类型 + # descstr=None + # modify_date: date + # updated_at2: date + # 结果变为: + # modify_date DATE, + # updated_at2 DATE, + # descstr VARCHAR(255) + + def __repr__(self): + return str(self.__dict__) + diff --git a/tests/exam/BatchInsertTest.py b/tests/exam/BatchInsertTest.py index b2f3eea..96fc31d 100644 --- a/tests/exam/BatchInsertTest.py +++ b/tests/exam/BatchInsertTest.py @@ -1,9 +1,9 @@ """ batch insert for student2 """ -from bee.api import Suid, SuidRich -from bee.sqllib import BeeSql +from bee.api import SuidRich import MyConfig +from bee.honeyfactory import BF from entity.Student2 import Student2 @@ -11,18 +11,21 @@ if __name__ == '__main__': print("start") MyConfig.init() - createSql = """ - CREATE TABLE student2 ( - id INTEGER PRIMARY KEY NOT NULL, - name VARCHAR(100), - age INT, - remark VARCHAR(100), - addr VARCHAR(100) - ); - """ - - # beeSql=BeeSql() - # beeSql.modify(createSql, []) + # createSql = """ + # CREATE TABLE student2 ( + # id INTEGER PRIMARY KEY NOT NULL, + # name VARCHAR(100), + # age INT, + # remark VARCHAR(100), + # addr VARCHAR(100) + # ); + # """ + # preparedSql=BF.preparedSql() + # preparedSql.modify(createSql, []) + + + suidRich=BF.suidRich() + suidRich.create_table(Student2,True) # since 1.6.0 student0=Student2() student0.name = "bee" diff --git a/tests/exam/BatchInsertTest3.py b/tests/exam/BatchInsertTest3.py index c76e0c9..c801e4e 100644 --- a/tests/exam/BatchInsertTest3.py +++ b/tests/exam/BatchInsertTest3.py @@ -28,7 +28,7 @@ if __name__ == '__main__': orders=Orders3() orders.name="bee130" orders.ext="aaa" #实体没有字段,会被忽略。出去安全考虑 - orders.id=10002 + orders.id=104 suid = Suid() n1= suid.update(orders) diff --git a/tests/exam/BatchInsertTest4.py b/tests/exam/BatchInsertTest4.py new file mode 100644 index 0000000..196be23 --- /dev/null +++ b/tests/exam/BatchInsertTest4.py @@ -0,0 +1,51 @@ +""" batch insert for student2 """ + +from bee.api import SuidRich + +import MyConfig +# from bee.honeyfactory import BF +from entity.Student3 import Student3 + + +if __name__ == '__main__': + print("start") + MyConfig.init() + + + + # suidRich=BF.suidRich() + # suidRich.create_table(Student3,True) # since 1.6.0 + + student0=Student3() + student0.name = "bee" + student0.age=20 + student0.remark= "bee" + + student1=Student3() + student1.name = "bee1" + student1.addr="" + student1.age=22 + student1.remark= "bee1" + + student2=Student3() + student2.name = "黄二" + student2.addr="" + student2.age=21 + + student3=Student3() + student3.name = "张三" + student3.addr="" + student3.age=21 + + + entity_list=[] + entity_list.append(student0) + entity_list.append(student1) + entity_list.append(student2) + entity_list.append(student3) + + suidRich = SuidRich() + insertNum = suidRich.insert_batch(entity_list) + print(insertNum) + + print("finished") diff --git a/tests/exam/SuidTest11Page.py b/tests/exam/SuidTest11Page.py index ae50d42..b70b0e8 100644 --- a/tests/exam/SuidTest11Page.py +++ b/tests/exam/SuidTest11Page.py @@ -1,5 +1,5 @@ # from org.teasoft.exam.entity.Test import Test -from bee.api import Suid, SuidRich +from bee.api import SuidRich from entity.Test import Test import MyConfig @@ -9,7 +9,7 @@ if __name__ == '__main__': print("start") MyConfig.init() # config = HoneyConfig() - # config.dbName="mysql" + # config.dbname="mysql" # orders=Orders(id=1, name="bee") orders=Test() diff --git a/tests/exam/SuidTest12_insert.py b/tests/exam/SuidTest12_insert.py index 09f33d6..f6b3ac2 100644 --- a/tests/exam/SuidTest12_insert.py +++ b/tests/exam/SuidTest12_insert.py @@ -9,11 +9,11 @@ if __name__ == '__main__': MyConfig.init() # config = HoneyConfig() - # config.dbName="mysql" + # config.dbname="mysql" # orders=Orders(id=1, name="bee") orders=Test() - orders.id=104 + orders.id=106 orders.name="bee" orders.remark="test" diff --git a/tests/exam/SuidTest13_insert.py b/tests/exam/SuidTest13_insert.py index 16520d3..914631f 100644 --- a/tests/exam/SuidTest13_insert.py +++ b/tests/exam/SuidTest13_insert.py @@ -9,7 +9,7 @@ if __name__ == '__main__': MyConfig.init() # config = HoneyConfig() - # config.dbName="mysql" + # config.dbname="mysql" # orders=Orders(id=1, name="bee") orders=Orders() diff --git a/tests/exam15/BatchInsertTest2.py b/tests/exam15/BatchInsertTest2.py index 36661c8..e1d200f 100644 --- a/tests/exam15/BatchInsertTest2.py +++ b/tests/exam15/BatchInsertTest2.py @@ -1,7 +1,6 @@ """ batch insert for orders """ from bee.api import SuidRich -from bee.sqllib import BeeSql import MyConfig from entity.Orders import Orders @@ -11,20 +10,24 @@ if __name__ == '__main__': print("start") MyConfig.init() - createSql = """ - CREATE TABLE orders ( - id INTEGER PRIMARY KEY NOT NULL, - name VARCHAR(100), - age INT, - remark VARCHAR(100), - ext VARCHAR(100) - ); - """ + # createSql = """ + # CREATE TABLE orders ( + # id INTEGER PRIMARY KEY NOT NULL, + # name VARCHAR(100), + # age INT, + # remark VARCHAR(100), + # ext VARCHAR(100) + # ); + # """ # beeSql=BeeSql() # # beeSql.modify(createSql, []) # beeSql.modify(createSql) + + suidRich = SuidRich() + suidRich.create_table(Orders, True) # would drop the table first + orders0=Orders() orders0.name = "bee" orders0.remark="remark test" @@ -37,7 +40,6 @@ if __name__ == '__main__': entity_list.append(orders0) entity_list.append(orders1) - suidRich = SuidRich() insertNum = suidRich.insert_batch(entity_list) print(insertNum) diff --git a/tests/exam15/DdlTest2.py b/tests/exam15/DdlTest2.py index a26baeb..3c0a878 100644 --- a/tests/exam15/DdlTest2.py +++ b/tests/exam15/DdlTest2.py @@ -1,5 +1,4 @@ from bee.api import SuidRich -from bee.config import PreConfig import MyConfig from entity.Orders_2025 import Orders_2025 @@ -9,9 +8,6 @@ if __name__ == '__main__': print("start") MyConfig.init() - #suggest set project root path for it - PreConfig.config_folder_root_path="E:\\JavaWeb\\eclipse-workspace202312\\BeePy-automvc\\tests\\exam" - suidRich = SuidRich() suidRich.create_table(Orders_2025) diff --git a/tests/exam15/DdlTest5.py b/tests/exam15/DdlTest5.py index f827776..37ae7e7 100644 --- a/tests/exam15/DdlTest5.py +++ b/tests/exam15/DdlTest5.py @@ -1,5 +1,4 @@ from bee.api import SuidRich -from bee.config import PreConfig import MyConfig from entity.Orders_202501 import Orders_202501 @@ -10,9 +9,6 @@ if __name__ == '__main__': print("start") MyConfig.init() - #suggest set project root path for it - PreConfig.config_folder_root_path="E:\\JavaWeb\\eclipse-workspace202312\\BeePy-automvc\\tests\\exam" - suidRich = SuidRich() suidRich.create_table(Orders_202501,True) diff --git a/tests/exam15/PreTest3.py b/tests/exam15/PreTest3.py index 9fed830..033c906 100644 --- a/tests/exam15/PreTest3.py +++ b/tests/exam15/PreTest3.py @@ -1,8 +1,8 @@ from bee.api import PreparedSql -from bee.config import PreConfig import MyConfig from entity.Orders import Orders +from bee.context import HoneyContext if __name__ == "__main__": @@ -15,6 +15,9 @@ if __name__ == "__main__": print(orders_list) sql = "update orders set name = ?, remark = ? where id = ?" + # placeholder=HoneyContext.get_placeholder() #in python different db have diffent placeholder + # sql=sql.replace("%s", placeholder) + # sql=sql.replace("?", placeholder) params = ('bee130', 'test-update', 1) updateNum = pre.modify(sql, params) print("updateNum:", updateNum) diff --git a/tests/exam15/PreTest4_test_placeholder.py b/tests/exam15/PreTest4_test_placeholder.py new file mode 100644 index 0000000..67f959d --- /dev/null +++ b/tests/exam15/PreTest4_test_placeholder.py @@ -0,0 +1,45 @@ +from bee.api import PreparedSql +from bee.context import HoneyContext + +import MyConfig +from entity.Orders import Orders + + +if __name__ == "__main__": + + MyConfig.init() + pre = PreparedSql() + + print("-------------------") + # orders_list = pre.select_dict("SELECT * FROM orders WHERE name=#{name} and id=#{id} and name=#{name}", Orders, params_dict={"name":"bee1", "id":4}) + # print(orders_list) + + sql = "update orders set name = ?, remark = ? where id = ?" + # placeholder=HoneyContext.get_placeholder() #in python different db have diffent placeholder + # sql=sql.replace("?", placeholder) + + params = ('bee130', 'test-update', 1) + updateNum = pre.modify(sql, params) + print("updateNum:", updateNum) + + + sql ="update orders set name = %s, remark = %s where id = %s" + params = ('bee130', 'test-update', 1) + updateNum = pre.modify(sql, params) + print("updateNum:", updateNum) + + # print("-------------------") + # orders_list = pre.select_dict("SELECT * FROM orders", Orders) + # print(orders_list) + # + # orders_list = pre.select_dict("SELECT * FROM orders where id = #{id}", Orders, params_dict={"id":1}) + # print(orders_list) + # + # sql = "update orders set name = #{name}, remark = #{remark} where id = #{id}" + # params_dict = {"id":1, "name":"newName", "remark":"remark2"} + # updateNum = pre.modify_dict(sql, params_dict) + # + # orders_list = pre.select_dict("SELECT * FROM orders where id=#{id}", Orders, params_dict={"id":1}) + # print(orders_list) + + print("finished") diff --git a/tests/exam15/SelectFun.py b/tests/exam15/SelectFun.py index faeab90..aebc104 100644 --- a/tests/exam15/SelectFun.py +++ b/tests/exam15/SelectFun.py @@ -1,8 +1,7 @@ -from bee.api import Suid, SuidRich -from bee.config import PreConfig -from bee.osql.enum import FunctionType +from bee.api import SuidRich import MyConfig +from bee.osql.bee_enum import FunctionType from entity.Orders import Orders @@ -12,7 +11,7 @@ if __name__ == '__main__': orders=Orders() orders.name = "bee" - orders.id=1 + # orders.id=1 suidRich = SuidRich() # suidRich.delete(Orders()) diff --git a/tests/exam15/SuidTest1.py b/tests/exam15/SuidTest1.py index 6299282..8575660 100644 --- a/tests/exam15/SuidTest1.py +++ b/tests/exam15/SuidTest1.py @@ -1,4 +1,4 @@ -from bee.api import Suid, SuidRich +from bee.api import SuidRich import MyConfig from entity.Orders import Orders diff --git a/tests/exam16/Create.py b/tests/exam16/Create.py new file mode 100644 index 0000000..1b7e9d6 --- /dev/null +++ b/tests/exam16/Create.py @@ -0,0 +1,28 @@ +from bee.api import SuidRich + +import MyConfig +from entity.Orders import Orders +from entity.Orders3 import Orders3 +from entity.Student2 import Student2 +from entity.Student3 import Student3 +from entity.full import Entity + + +# from bee.util import HoneyUtil +if __name__ == '__main__': + + # create_sql=HoneyUtil.get_create_sql(Entity) + + MyConfig.init() + suidRich = SuidRich() + + #有声明类型和无声明类型都有 + suidRich.create_table(Entity,True) + + #无声明类型 + suidRich.create_table(Orders,True) + + # suidRich.create_table(Orders3,True) + + # suidRich.create_table(Student2,True) + # suidRich.create_table(Student3,True) \ No newline at end of file diff --git a/tests/exam16/SuidTest1.py b/tests/exam16/SuidTest1.py index 5bad056..7673be2 100644 --- a/tests/exam16/SuidTest1.py +++ b/tests/exam16/SuidTest1.py @@ -1,7 +1,7 @@ # from org.teasoft.exam.entity.Orders import Orders # from bee.api import Suid -from bee.osql.enum import Op +from bee.osql.bee_enum import Op import MyConfig from bee.honeyfactory import BF @@ -28,7 +28,7 @@ if __name__ == '__main__': condition = BF.condition() condition.op("name", Op.ne, "bee1").op("remark", Op.ne, "new2") - orderList = suid.select2(orders,condition) + orderList = suid.select(orders,condition) for one in orderList: print(one) diff --git a/tests/exam16/SuidTest2.py b/tests/exam16/SuidTest2.py index 5336562..3ad52c7 100644 --- a/tests/exam16/SuidTest2.py +++ b/tests/exam16/SuidTest2.py @@ -1,6 +1,6 @@ # from org.teasoft.exam.entity.Orders import Orders # from bee.api import Suid -from bee.osql.enum import Op +from bee.osql.bee_enum import Op import MyConfig from bee.honeyfactory import BF @@ -24,7 +24,7 @@ if __name__ == '__main__': condition = BF.condition() condition.op("name", Op.ne, "bee1").between("age", 20, 28) - orderList = suid.select2(stu,condition) + orderList = suid.select(stu,condition) for one in orderList: print(one) @@ -32,20 +32,20 @@ if __name__ == '__main__': condition = BF.condition() condition.op("name", Op.ne, "bee1").or_() condition.l_parentheses().between("age", 20, 28).r_parentheses() - orderList = suid.select2(stu,condition) + orderList = suid.select(stu,condition) for one in orderList: print(one) condition = BF.condition() condition.opWithField("name", Op.eq, "remark") - orderList = suid.select2(stu,condition) + orderList = suid.select(stu,condition) for one in orderList: print(one) # condition = BF.condition() # condition.groupBy("name") - # orderList = suid.select2(stu,condition) + # orderList = suid.select(stu,condition) # for one in orderList: # print(one) diff --git a/tests/exam16/SuidTest3.py b/tests/exam16/SuidTest3.py new file mode 100644 index 0000000..5b80b61 --- /dev/null +++ b/tests/exam16/SuidTest3.py @@ -0,0 +1,19 @@ +from entity.Orders import Orders + +if __name__ == '__main__': + print("start") + + # MyConfig.init() + + # orders=Orders() + # orders.name = "bee" + + # print(Orders.__annotations__) + anno={} + try: + anno =Orders.__annotations__ + except Exception: + pass + print(anno) + + print("finished") diff --git a/tests/exam16/SuidTest4.py b/tests/exam16/SuidTest4.py new file mode 100644 index 0000000..24f5c9a --- /dev/null +++ b/tests/exam16/SuidTest4.py @@ -0,0 +1,33 @@ +class Orders: + #__tablename__="orders6" + id = None + name = None + remark = None + # remark: str = None + + # __pk__="id" #aaa + # __pk__ = "name" # aaa + # __primary_key__="name" #aaa + + #can ignore + def __repr__(self): + return str(self.__dict__) + + +if __name__ == '__main__': + print("start") + + # MyConfig.init() + + # orders=Orders() + # orders.name = "bee" + + # print(Orders.__annotations__) + anno={} + try: + anno =Orders.__annotations__ + except Exception: + pass + print(anno) + + print("finished") diff --git a/tests/exam16/SuidTest5.py b/tests/exam16/SuidTest5.py new file mode 100644 index 0000000..dd7738f --- /dev/null +++ b/tests/exam16/SuidTest5.py @@ -0,0 +1,33 @@ +class Orders: + #__tablename__="orders6" + id = None + name = None + remark = None + # remark: str = None + + # __pk__="id" #aaa + # __pk__ = "name" # aaa + # __primary_key__="name" #aaa + + #can ignore + def __repr__(self): + return str(self.__dict__) + + +if __name__ == '__main__': + print("start") + + # MyConfig.init() + + # orders=Orders() + # orders.name = "bee" + anno={} + try: + anno =Orders.__annotations__ + except Exception: + pass + + print(anno) + + + print("finished") diff --git a/tests/exam16/bugTest.py b/tests/exam16/bugTest.py new file mode 100644 index 0000000..8358b79 --- /dev/null +++ b/tests/exam16/bugTest.py @@ -0,0 +1,31 @@ +import MyConfig +from bee.honeyfactory import BF +from entity.Orders import Orders + + +#test cache +if __name__ == '__main__': + print("start") + + MyConfig.init() + + # orders=Orders() + # # orders = Test() + # orders.id=1 + # orders.name = "bee" + + orders="" #test empty string entity + suid = BF.suid() + orderList = suid.select(orders) #test + if orderList: + for one in orderList: + print(one) + else: + print("empty result") + + orderList = suid.select(None) #test + if orderList: + for one in orderList: + print(one) + else: + print("empty result") \ No newline at end of file diff --git a/tests/exam16/cacheTest.py b/tests/exam16/cacheTest.py new file mode 100644 index 0000000..82f52d2 --- /dev/null +++ b/tests/exam16/cacheTest.py @@ -0,0 +1,47 @@ +import MyConfig +from bee.honeyfactory import BF +from entity.Orders import Orders + + +#test cache +if __name__ == '__main__': + print("start") + + MyConfig.init() + + orders=Orders() + # orders = Test() + orders.id=1 + orders.name = "bee" + + suid = BF.suid() + orderList = suid.select(orders) #test + for one in orderList: + print(one) + + #test cache + orderList = suid.select(orders) #test + for one in orderList: + print(one) + + orders2=Orders() + orders2.id=2 + orders2.name = "bee2" + suid.insert(orders2) + + #test cache + orderList = suid.select(orders) #test + for one in orderList: + print(one) + + # condition = BF.condition() + # condition.op("name", Op.ne, "bee1").op("remark", Op.ne, "new2") + # orderList = suid.select(orders,condition) + # for one in orderList: + # print(one) + + + + + + print("finished") diff --git a/tests/exam16/cacheTest2.py b/tests/exam16/cacheTest2.py new file mode 100644 index 0000000..3b89774 --- /dev/null +++ b/tests/exam16/cacheTest2.py @@ -0,0 +1,45 @@ +from bee.context import HoneyContext + +import MyConfig +from bee.honeyfactory import BF +from bee.osql.bee_enum import LocalType +from bee.osql.cache import CacheUtil +from entity.Orders import Orders +from entity.Orders2 import Orders2 + + +#test cache +# 虽然同表,但不同entity也不能共缓存 +if __name__ == '__main__': + print("start") + + MyConfig.init() + + orders=Orders() + # orders = Test() + orders.id=1 + orders.name = "bee" + + suid = BF.suid() + orderList = suid.select(orders) #test + for one in orderList: + print(one) + + + orders=Orders() + # orders = Test() + orders.id=1 + orders.name = "bee" + + # 虽然同表,但不同entity也不能共缓存 + + #test cache + orderList = suid.select(orders) #test + for one in orderList: + print(one) + + + # print(CacheUtil._getMap()) + print(HoneyContext._get_storage()) + + print("finished") diff --git a/tests/exam16/cacheTest3.py b/tests/exam16/cacheTest3.py new file mode 100644 index 0000000..5be483a --- /dev/null +++ b/tests/exam16/cacheTest3.py @@ -0,0 +1,64 @@ +from bee.context import HoneyContext +import time +import MyConfig +from bee.honeyfactory import BF +from bee.osql.cache import CacheUtil +from entity.Orders import Orders +from entity.Orders2 import Orders2 + + +#test: CacheUtil cannot be subclassed +# class Cache2(CacheUtil): +# pass + + +#test cache +# 虽然同表,但不同entity也不能共缓存 +if __name__ == '__main__': + print("start") + + # c2=Cache2() + + MyConfig.init() + + orders=Orders() + # orders = Test() + orders.id=1 + orders.name = "bee" + + suid = BF.suid() + orderList = suid.select(orders) #test + for one in orderList: + print(one) + + time.sleep(10) + + orders=Orders2() + # orders = Test() + orders.id=2 + orders.name = "bee" + + # 虽然同表,但不同entity也不能共缓存 + + #test cache + orderList = suid.select(orders) #test + for one in orderList: + print(one) + + + + orders=Orders() + # orders = Test() + orders.id=1 + orders.name = "bee" + + suid = BF.suid() + orderList = suid.select(orders) #test + for one in orderList: + print(one) + + + # print(CacheUtil._getMap()) + print(HoneyContext._get_storage()) + + print("finished") diff --git a/tests/exam16/cacheTest4.py b/tests/exam16/cacheTest4.py new file mode 100644 index 0000000..95b401b --- /dev/null +++ b/tests/exam16/cacheTest4.py @@ -0,0 +1,85 @@ +from bee.context import HoneyContext +import time +import MyConfig +from bee.honeyfactory import BF +from bee.osql.cache import CacheUtil +from entity.Orders import Orders +from entity.Orders2 import Orders2 + + +#test: CacheUtil cannot be subclassed +# class Cache2(CacheUtil): +# pass + + +#test cache +# 虽然同表,但不同entity也不能共缓存 +if __name__ == '__main__': + print("start") + + # c2=Cache2() + + MyConfig.init() + + orders=Orders() + # orders = Test() + orders.id=1 + orders.name = "bee" + + suid = BF.suid() + + for i in range(8): + orders.id=i + orderList = suid.select(orders) #test + if orderList: + for one in orderList: + print(one) + + time.sleep(10) + + orders.id=1 + orderList = suid.select(orders) #test + if orderList: + for one in orderList: + print(one) + + orders.id=9 + orderList = suid.select(orders) #test + if orderList: + for one in orderList: + print(one) + + orders=Orders2() + # orders = Test() + orders.id=2 + orders.name = "bee" + + # 虽然同表,但不同entity也不能共缓存 + + #test cache + orderList = suid.select(orders) #test + for one in orderList: + print(one) + + + + orders=Orders() + # orders = Test() + orders.id=1 + orders.name = "bee" + + suid = BF.suid() + orderList = suid.select(orders) #test + for one in orderList: + print(one) + + suid = BF.suid() + orderList = suid.select(orders) #test + for one in orderList: + print(one) + + + # print(CacheUtil._getMap()) + print(HoneyContext._get_storage()) + + print("finished") diff --git a/tests/exam16/conditionTest.py b/tests/exam16/conditionTest.py index 3c8778f..ffa2bd0 100644 --- a/tests/exam16/conditionTest.py +++ b/tests/exam16/conditionTest.py @@ -2,10 +2,10 @@ # from bee.api import Suid # from bee.config import PreConfig -from bee.osql.enum import Op, OrderType, FunctionType import MyConfig from bee.honeyfactory import BF +from bee.osql.bee_enum import Op, FunctionType from entity.Student2 import Student2 @@ -23,7 +23,7 @@ if __name__ == '__main__': # condition = BF.condition() # condition.op("name", Op.ne, "bee1").between("age", 20, 28) - # orderList = suid.select2(stu,condition) + # orderList = suid.select(stu,condition) # for one in orderList: # print(one) # @@ -31,36 +31,36 @@ if __name__ == '__main__': # condition = BF.condition() # condition.op("name", Op.ne, "bee1").or_() # condition.l_parentheses().between("age", 20, 28).r_parentheses() - # orderList = suid.select2(stu,condition) + # orderList = suid.select(stu,condition) # for one in orderList: # print(one) # # # condition = BF.condition() # condition.opWithField("name", Op.eq, "remark") - # orderList = suid.select2(stu,condition) + # orderList = suid.select(stu,condition) # for one in orderList: # print(one) # PreConfig.sql_key_word_case="upper" condition = BF.condition() - condition.selectField("name,age") + condition.selectField("name,count(*) as remark") condition.op("name", Op.ne, "bee1") condition.groupBy("name") # having(FunctionType.MIN, "field", Op.ge, 60)-->having min(field)>=60 - condition.having(FunctionType.MIN, "age", Op.ge, 25) + condition.having(FunctionType.MIN, "age", Op.ge, 21) condition.having(FunctionType.MAX, "age", Op.lt, 30) condition.orderBy("name") - condition.orderBy2("age",OrderType.DESC) - orderList = suid.select2(stu,condition) + # condition.orderBy2("age",OrderType.DESC) + orderList = suid.select(stu,condition) for one in orderList: print(one) # condition = BF.condition() # condition.groupBy("name") - # orderList = suid.select2(stu,condition) + # orderList = suid.select(stu,condition) # for one in orderList: # print(one) diff --git a/tests/exam16/conditionTest2.py b/tests/exam16/conditionTest2.py index e2cfdf4..5a18380 100644 --- a/tests/exam16/conditionTest2.py +++ b/tests/exam16/conditionTest2.py @@ -1,16 +1,10 @@ -# from org.teasoft.exam.entity.Orders import Orders -# from bee.api import Suid - -# from bee.config import PreConfig -from bee.osql.enum import Op +from bee.osql.bee_enum import Op import MyConfig from bee.honeyfactory import BF from entity.Student2 import Student2 -# from bee.config import PreConfig -# from org.teasoft.exam.entity.Test import Test if __name__ == '__main__': print("start") @@ -24,7 +18,7 @@ if __name__ == '__main__': # empty condition condition = BF.condition() - orderList = suid.select2(stu,condition) + orderList = suid.select(stu,condition) for one in orderList: print(one) @@ -32,7 +26,7 @@ if __name__ == '__main__': condition = BF.condition() # condition.op("remark", Op.eq, None) condition.op("addr", Op.eq, None) - orderList = suid.select2(stu,condition) + orderList = suid.select(stu,condition) for one in orderList: print(one) diff --git a/tests/exam16/conditionTest3_paging.py b/tests/exam16/conditionTest3_paging.py index ba357d1..b8e8f64 100644 --- a/tests/exam16/conditionTest3_paging.py +++ b/tests/exam16/conditionTest3_paging.py @@ -1,4 +1,4 @@ -from bee.osql.enum import Op +from bee.osql.bee_enum import Op import MyConfig from bee.honeyfactory import BF @@ -18,17 +18,18 @@ if __name__ == '__main__': # # empty condition # condition = BF.condition() - # orderList = suid.select2(stu,condition) + # orderList = suid.select(stu,condition) # for one in orderList: # print(one) # field is null condition = BF.condition() # condition.op("remark", Op.eq, None) + # condition.op("--addr", Op.eq, None) condition.op("addr", Op.eq, None) condition.start(10).size(10) # condition.start(5) - orderList = suid.select2(stu,condition) + orderList = suid.select(stu,condition) for one in orderList: print(one) @@ -38,7 +39,7 @@ if __name__ == '__main__': condition.op("addr", Op.eq, None) # condition.start(0).size(10) condition.start(5) - orderList = suid.select2(stu,condition) + orderList = suid.select(stu,condition) for one in orderList: print(one) @@ -48,7 +49,7 @@ if __name__ == '__main__': # condition.start(0).size(10) # condition.start(5) condition.size(10) - orderList = suid.select2(stu,condition) + orderList = suid.select(stu,condition) for one in orderList: print(one) @@ -58,7 +59,7 @@ if __name__ == '__main__': condition.op("addr", Op.eq, None) # condition.start("") condition.size(10) - orderList = suid.select2(stu,condition) + orderList = suid.select(stu,condition) for one in orderList: print(one) diff --git a/tests/exam16/conditionTest4_delete.py b/tests/exam16/conditionTest4_delete.py index d1e6ab2..d097c76 100644 --- a/tests/exam16/conditionTest4_delete.py +++ b/tests/exam16/conditionTest4_delete.py @@ -1,4 +1,4 @@ -from bee.osql.enum import Op, FunctionType +from bee.osql.bee_enum import Op import MyConfig from bee.honeyfactory import BF @@ -17,7 +17,7 @@ if __name__ == '__main__': # empty condition # condition = BF.condition() - # orderList = suid.select2(stu,condition) + # orderList = suid.select(stu,condition) # for one in orderList: # print(one) @@ -26,11 +26,11 @@ if __name__ == '__main__': # condition.op("remark", Op.eq, None) condition.op("addr", Op.eq, None) - orderList = suid.select2(stu,condition) + orderList = suid.select(stu,condition) for one in orderList: print(one) - delNum = suid.delete2(stu,condition) + delNum = suid.delete(stu,condition) print(delNum) @@ -38,7 +38,7 @@ if __name__ == '__main__': condition = BF.condition() condition.op("name", Op.eq, "黄二") - orderList = suid.select2(stu,condition) + orderList = suid.select(stu,condition) for one in orderList: print(one) @@ -46,7 +46,7 @@ if __name__ == '__main__': # condition.groupBy("name") # condition.having(FunctionType.MAX, "age", Op.lt, 30) # condition.orderBy("name") - delNum = suid.delete2(stu,condition) + delNum = suid.delete(stu,condition) print(delNum) diff --git a/tests/exam16/conditionTest5_update.py b/tests/exam16/conditionTest5_update.py index 527c0a0..b344c3d 100644 --- a/tests/exam16/conditionTest5_update.py +++ b/tests/exam16/conditionTest5_update.py @@ -1,9 +1,12 @@ -from bee.osql.enum import Op +from bee.osql.bee_enum import Op import MyConfig from bee.honeyfactory import BF from entity.Student2 import Student2 +#update set case 0: +#entity no value +#donot use condtion setXxx if __name__ == '__main__': print("start") @@ -20,7 +23,7 @@ if __name__ == '__main__': # condition.op("remark", Op.eq, None) condition.op("addr", Op.eq, None) - orderList = suidRich.select2(stu,condition) + orderList = suidRich.select(stu,condition) for one in orderList: print(one) diff --git a/tests/exam16/conditionTest5_update1.py b/tests/exam16/conditionTest5_update1.py new file mode 100644 index 0000000..88339c7 --- /dev/null +++ b/tests/exam16/conditionTest5_update1.py @@ -0,0 +1,45 @@ +from bee.osql.bee_enum import Op + +import MyConfig +from bee.honeyfactory import BF +from entity.Student2 import Student2 + +#update set case 1: +#entity have value (diff case 0) +#donot use condtion setXxx +if __name__ == '__main__': + print("start") + + MyConfig.init() + + stu=Student2() + stu.age=11 + + suidRich = BF.suidRich() + + + # field is null + condition = BF.condition() + # condition.op("remark", Op.eq, None) + condition.op("addr", Op.eq, None) + + orderList = suidRich.select(stu,condition) + for one in orderList: + print(one) + + stu.addr="use new addr" + stu.remark="bee" + # updateNum = suidRich.updateBy(stu,condition,"remark") + # updateNum = suidRich.updateBy(stu,condition,"id") + updateNum = suidRich.updateBy(stu,condition,"name") + # updateNum = suidRich.updateBy(stu,condition) # check + # updateNum = suidRich.updateBy(stu,condition,"")# check + # updateNum = suidRich.updateBy(stu,condition," ")# check + print(updateNum) + # updateBy SQL: update student2 set age = ?, addr = ?, remark = ? where name is null and addr is null + + orderList = suidRich.select(stu) + for one in orderList: + print(one) + + print("finished") diff --git a/tests/exam16/conditionTest5_update2.py b/tests/exam16/conditionTest5_update2.py new file mode 100644 index 0000000..560bb0c --- /dev/null +++ b/tests/exam16/conditionTest5_update2.py @@ -0,0 +1,46 @@ +from bee.osql.bee_enum import Op + +import MyConfig +from bee.honeyfactory import BF +from entity.Student2 import Student2 + +#update set case 2: +#entity no value +#use condtion where op and setXxx +if __name__ == '__main__': + print("start") + + MyConfig.init() + + #entity中,非空的属性,声明在whereFields会转到where中,其它非空的属性转在update set + stu=Student2() + + suidRich = BF.suidRich() + + + + condition = BF.condition() + #condition中过滤条件都会转在where + condition.op("remark", Op.eq, "") + condition.op("addr", Op.eq, None) # filter/where + + #使用condition中set开头的方法,都会用在update set + condition.set("addr", "use new addr") # update set + condition.set("remark", None) # update set remark=null + + + + orderList = suidRich.select(stu,condition) + for one in orderList: + print(one) + + updateNum = suidRich.updateBy(stu,condition,"name") + print(updateNum) +# [INFO] [Bee] sql>>> updateBy SQL: update student2 set addr = ?,remark = ? where name is null and remark = ? and addr is null +# [INFO] [Bee] sql>>> params: ['use new addr', None, ''] + + orderList = suidRich.select(stu) + for one in orderList: + print(one) + + print("finished") diff --git a/tests/exam16/conditionTest5_update3.py b/tests/exam16/conditionTest5_update3.py new file mode 100644 index 0000000..eed3666 --- /dev/null +++ b/tests/exam16/conditionTest5_update3.py @@ -0,0 +1,47 @@ +from bee.osql.bee_enum import Op + +import MyConfig +from bee.honeyfactory import BF +from entity.Student2 import Student2 + +#update set case 3: +#entity have value(diff 2) +#use condtion where op and setXxx +if __name__ == '__main__': + print("start") + + MyConfig.init() + + #entity中,非空的属性,声明在whereFields会转到where中,其它非空的属性转在update set + stu=Student2() + stu.age=12 + + suidRich = BF.suidRich() + + + + condition = BF.condition() + #condition中过滤条件都会转在where + condition.op("remark", Op.eq, "") + condition.op("addr", Op.eq, None) # filter/where + + #使用condition中set开头的方法,都会用在update set + condition.set("addr", "use new addr") # update set + condition.set("remark", None) # update set remark=null + + + + orderList = suidRich.select(stu,condition) + for one in orderList: + print(one) + + updateNum = suidRich.updateBy(stu,condition,"name") + print(updateNum) +# [INFO] [Bee] sql>>> updateBy SQL: update student2 set age = ? , addr = ?,remark = ? where name is null and remark = ? and addr is null +# [INFO] [Bee] sql>>> params: [12, 'use new addr', None, ''] + + orderList = suidRich.select(stu) + for one in orderList: + print(one) + + print("finished") diff --git a/tests/exam16/conditionTest5_update4.py b/tests/exam16/conditionTest5_update4.py new file mode 100644 index 0000000..13fbd55 --- /dev/null +++ b/tests/exam16/conditionTest5_update4.py @@ -0,0 +1,41 @@ +import MyConfig +from bee.honeyfactory import BF +from entity.Student2 import Student2 + +#update set case 4: +#entity have value +#use condtion where between and setXxx +if __name__ == '__main__': + print("start") + + MyConfig.init() + + #entity中,非空的属性,声明在whereFields会转到where中,其它非空的属性转在update set + stu=Student2() + stu.addr="use new addr" + + condition = BF.condition() + #condition中过滤条件都会转在where + # condition.op("remark", Op.eq, "") + condition.between("age", 10, 15) + + #使用condition中set开头的方法,都会用在update set + condition.set("addr", "use new addr-update2") # update set + # condition.set("remark", None) # update set remark=null + + + suidRich = BF.suidRich() + orderList = suidRich.select(stu,condition) + for one in orderList: + print(one) + + updateNum = suidRich.updateBy(stu,condition,"remark") + print(updateNum) +# [INFO] [Bee] sql>>> updateBy SQL: update student2 set addr = ? , addr = ? where remark is null and age between ? and ? +# [INFO] [Bee] sql>>> params: ['use new addr', 'use new addr-update2', 10, 15] + + orderList = suidRich.select(stu) + for one in orderList: + print(one) + + print("finished") diff --git a/tests/exam16/conditionTest5_update4_3.py b/tests/exam16/conditionTest5_update4_3.py new file mode 100644 index 0000000..1962153 --- /dev/null +++ b/tests/exam16/conditionTest5_update4_3.py @@ -0,0 +1,43 @@ +import MyConfig +from bee.honeyfactory import BF +from entity.Student3 import Student3 + +#update set case 4: +#entity have value +#use condtion where between and setXxx +if __name__ == '__main__': + print("start") + + MyConfig.init() + + suidRich = BF.suidRich() + # suidRich.create_table(Student3, True) + + #entity中,非空的属性,声明在whereFields会转到where中,其它非空的属性转在update set + stu=Student3() + stu.addr="" + + condition = BF.condition() + #condition中过滤条件都会转在where + # condition.op("remark", Op.eq, "") + condition.between("age", 20, 21) + + #使用condition中set开头的方法,都会用在update set + condition.set("addr", "use new addr-update2") # update set + # condition.set("remark", None) # update set remark=null + + + orderList = suidRich.select(stu,condition) + for one in orderList: + print(one) + + updateNum = suidRich.updateBy(stu,condition,"remark") + print(updateNum) +# [INFO] [Bee] sql>>> updateBy SQL: update student3 set addr = %s , addr = %s where remark is null and age between %s and %s +# [INFO] [Bee] sql>>> params: ['', 'use new addr-update2', 20, 21] + + orderList = suidRich.select(stu) + for one in orderList: + print(one) + + print("finished") diff --git a/tests/exam16/conditionTest5_update5.py b/tests/exam16/conditionTest5_update5.py new file mode 100644 index 0000000..6882897 --- /dev/null +++ b/tests/exam16/conditionTest5_update5.py @@ -0,0 +1,38 @@ +import MyConfig +from bee.honeyfactory import BF +from entity.Student2 import Student2 + +#update set case 5: +#entity no value +#use condtion just setXxx; no op for where +if __name__ == '__main__': + print("start") + + MyConfig.init() + + #entity中,非空的属性,声明在whereFields会转到where中,其它非空的属性转在update set + stu=Student2() + + condition = BF.condition() + #condition中过滤条件都会转在where + # condition.op("remark", Op.eq, "") + + #使用condition中set开头的方法,都会用在update set + condition.set("addr", "use new addr") # update set + condition.set("age", 12) + + suidRich = BF.suidRich() + orderList = suidRich.select(stu,condition) + for one in orderList: + print(one) + + updateNum = suidRich.updateBy(stu,condition,"age") + print(updateNum) +# [INFO] [Bee] sql>>> updateBy SQL: update student2 set addr = ?,age = ? where age is null +# [INFO] [Bee] sql>>> params: ['use new addr', 12] + + orderList = suidRich.select(stu) + for one in orderList: + print(one) + + print("finished") diff --git a/tests/exam16/conditionTest5_update6.py b/tests/exam16/conditionTest5_update6.py new file mode 100644 index 0000000..0fadbe5 --- /dev/null +++ b/tests/exam16/conditionTest5_update6.py @@ -0,0 +1,41 @@ +import MyConfig +from bee.honeyfactory import BF +from entity.Student2 import Student2 + +#update set case 6: +#entity have value +#use condtion setXxx; no where op +# whereFields have more than one +if __name__ == '__main__': + print("start") + + MyConfig.init() + + #entity中,非空的属性,声明在whereFields会转到where中,其它非空的属性转在update set + stu=Student2() + stu.age=10 + stu.addr="use new addr" + + condition = BF.condition() + #condition中过滤条件都会转在where + # condition.op("remark", Op.eq, "") + + #使用condition中set开头的方法,都会用在update set + condition.set("addr", "use new addr-update") # update set + condition.set("age", 12) + + suidRich = BF.suidRich() + orderList = suidRich.select(stu,condition) + for one in orderList: + print(one) + + updateNum = suidRich.updateBy(stu,condition,"age","addr") #whereFields have more than one + print(updateNum) +# [INFO] [Bee] sql>>> updateBy SQL: update student2 set addr = ?,age = ? where age = ? and addr = ? +# [INFO] [Bee] sql>>> params: ['use new addr-update', 12, 10, 'use new addr'] + + orderList = suidRich.select(stu) + for one in orderList: + print(one) + + print("finished") diff --git a/tests/exam16/conditionTest5_update7.py b/tests/exam16/conditionTest5_update7.py new file mode 100644 index 0000000..cd55ff1 --- /dev/null +++ b/tests/exam16/conditionTest5_update7.py @@ -0,0 +1,43 @@ +from bee.osql.bee_enum import Op + +import MyConfig +from bee.honeyfactory import BF +from entity.Student2 import Student2 + +#update set case 7: +#entity have value +#use condtion setXxx; also where op +# whereFields have more than one +if __name__ == '__main__': + print("start") + + MyConfig.init() + + #entity中,非空的属性,声明在whereFields会转到where中,其它非空的属性转在update set + stu=Student2() + stu.age=10 + stu.addr="use new addr" + + condition = BF.condition() + #condition中过滤条件都会转在where + condition.op("remark", Op.eq, "") + + #使用condition中set开头的方法,都会用在update set + condition.set("addr", "use new addr-update") # update set + condition.set("age", 12) + + suidRich = BF.suidRich() + orderList = suidRich.select(stu,condition) + for one in orderList: + print(one) + + updateNum = suidRich.updateBy(stu,condition,"age","addr") #whereFields have more than one + print(updateNum) +# [INFO] [Bee] sql>>> updateBy SQL: update student2 set addr = ?,age = ? where age = ? and addr = ? and remark = ? +# [INFO] [Bee] sql>>> params: ['use new addr-update', 12, 10, 'use new addr', ''] + + orderList = suidRich.select(stu) + for one in orderList: + print(one) + + print("finished") diff --git a/tests/exam16/conditionTest5_update8.py b/tests/exam16/conditionTest5_update8.py new file mode 100644 index 0000000..e1b6927 --- /dev/null +++ b/tests/exam16/conditionTest5_update8.py @@ -0,0 +1,44 @@ +import MyConfig +from bee.honeyfactory import BF +from entity.Student2 import Student2 + +#update set case 8: +# condition=None +if __name__ == '__main__': + print("start") + + MyConfig.init() + + suidRich = BF.suidRich() + + #entity中,非空的属性,声明在whereFields会转到where中,其它非空的属性转在update set + stu=Student2() + stu.age=10 + stu.addr="use new addr" + + # condition = BF.condition() + # #condition中过滤条件都会转在where + # condition.op("remark", Op.eq, "") + # + # #使用condition中set开头的方法,都会用在update set + # condition.set("addr", "use new addr-update") # update set + # condition.set("age", 12) + # + # + # orderList = suidRich.select(stu,condition) + # for one in orderList: + # print(one) + + condition=None + + # updateNum = suidRich.updateBy(stu,condition,"age") + updateNum = suidRich.updateBy(stu,condition,"age","name") + print(updateNum) +# [INFO] [Bee] sql>>> updateBy SQL: update student2 set addr = ?,age = ? where age = ? and addr = ? and remark = ? +# [INFO] [Bee] sql>>> params: ['use new addr-update', 12, 10, 'use new addr', ''] + + orderList = suidRich.select(stu) + for one in orderList: + print(one) + + print("finished") diff --git a/tests/exam16/config_test.py b/tests/exam16/config_test.py new file mode 100644 index 0000000..d2fe31c --- /dev/null +++ b/tests/exam16/config_test.py @@ -0,0 +1,14 @@ +from bee.config import HoneyConfig + +import MyConfig + +if __name__ == '__main__': + + print("start") + MyConfig.init() + print("-----------------") + config = HoneyConfig() # how to call first time + # config = HoneyConfig() # how to call first time + HoneyConfig.cache_max_size + print(HoneyConfig.cache_max_size) + print("end") \ No newline at end of file diff --git a/tests/exam16/full_type.py b/tests/exam16/full_type.py new file mode 100644 index 0000000..c860adb --- /dev/null +++ b/tests/exam16/full_type.py @@ -0,0 +1,78 @@ +from bee.config import HoneyConfig + +import MyConfig +from bee.honeyfactory import BF +import datetime as dt +from entity.full import Entity + + +if __name__ == '__main__': + + print("start") + + MyConfig.init() + + old_naming_translate_type= HoneyConfig.naming_translate_type + HoneyConfig.naming_translate_type=3 + + # also can set in bee.properties + # bee.naming_translate_type = 3 + + myTime = dt.time(12,34,59)#dt.time(时、分、秒) + + entity=Entity() + entity.id=4 + entity.name="full-type-test" + entity.name2="name2" + # entity.flag=False + entity.flag=True + entity.price=3.12 + entity.descstr="desc" + entity.remark="remark" + entity.modify_date=dt.datetime.today() + entity.updated_time=myTime #sqlite不支持time + entity.created_at=dt.datetime.today() + + entity.map={'name':"John","age":12} + entity.list0=[1, 2, 3,'a', 4, 5 ] + entity.tuple0=(1, 2, 3,'b', 4, '5') + entity.set0="" + + entity.map1={'name':"John","age":12} + entity.list1=[1, 2, 3,'a', 4, 5 ] + entity.tuple1=(1, 2, 3,'b', 4, '5') + entity.set1={1, 2, 'c', 4, 3, '5'} + + print(type(entity.map)) + + entity.ext="ext" #test ext field + + + # print(entity.modify_date) + # print(entity.created_at) + + + + print(entity.__dict__) + print(Entity.__dict__) + # fields = [] + # values = [] + # for name, value in entity.__dict__.items(): + # if value is not None: + # fields.append(name) + # values.append(value) + # print(fields) + # print(values) + + suidRich=BF.suidRich() + suidRich.insert(entity) + + # List = suidRich.select(entity) + List = suidRich.select(Entity()) + for one in List: + print(one) + + # reset + HoneyConfig.naming_translate_type=old_naming_translate_type + + diff --git a/tests/exam16/full_type2.py b/tests/exam16/full_type2.py new file mode 100644 index 0000000..cb8a7d3 --- /dev/null +++ b/tests/exam16/full_type2.py @@ -0,0 +1,66 @@ +from bee.config import HoneyConfig + +import MyConfig +from bee.honeyfactory import BF +import datetime as dt +from entity.full import Entity + + +if __name__ == '__main__': + + print("start") + + MyConfig.init() + old_naming_translate_type= HoneyConfig.naming_translate_type + HoneyConfig.naming_translate_type=3 + + + myTime = dt.time(12,34,59)#dt.time(时、分、秒) + + entity=Entity() + entity.id=4 + entity.name="full-type-test" + entity.name2="name2" + # entity.flag=False + entity.flag=True + entity.price=3.12 + entity.descstr="desc" + entity.remark="remark" + entity.modify_date=dt.datetime.today() + entity.updated_time=myTime #sqlite不支持time + entity.created_at=dt.datetime.today() + + entity.map={'name':"John","age":12} + print(type(entity.map)) + + entity.ext="ext" #test ext field + + + # print(entity.modify_date) + # print(entity.created_at) + + + + # print(entity.__dict__) + # print(Entity.__dict__) + # fields = [] + # values = [] + # for name, value in entity.__dict__.items(): + # if value is not None: + # fields.append(name) + # values.append(value) + # print(fields) + # print(values) + + suidRich=BF.suidRich() + suidRich.insert(entity) + + List = suidRich.select(entity) + List = suidRich.select(Entity()) + for one in List: + print(one) + + # reset + HoneyConfig.naming_translate_type=old_naming_translate_type + + diff --git a/tests/exam16/full_type3.py b/tests/exam16/full_type3.py new file mode 100644 index 0000000..40c76dd --- /dev/null +++ b/tests/exam16/full_type3.py @@ -0,0 +1,63 @@ +from bee.config import HoneyConfig + +import MyConfig +from bee.honeyfactory import BF +import datetime as dt +from entity.full import Entity + + +#批量插入 +if __name__ == '__main__': + + print("start") + + MyConfig.init() + old_naming_translate_type= HoneyConfig.naming_translate_type + HoneyConfig.naming_translate_type=3 + + + myTime = dt.time(12,34,59)#dt.time(时、分、秒) + + entity=Entity() + entity.id=8 + entity.name="full-type-test" + entity.name2="name2" + # entity.flag=False + entity.flag=True + entity.price=3.12 + entity.descstr="desc" + entity.remark="remark" + entity.modify_date=dt.datetime.today() + entity.updated_time=myTime #sqlite不支持time + entity.created_at=dt.datetime.today() + entity.ext="ext" #test ext field + + entity2=Entity() + entity2.id=9 + entity2.name="full-type-test" + entity2.name2="name2" + # entity2.flag=False + entity2.flag=True + entity2.price=3.12 + entity2.descstr="desc" + entity2.remark="remark" + entity2.modify_date=dt.datetime.today() + entity2.updated_time=myTime #sqlite不支持time + entity2.created_at=dt.datetime.today() + entity2.ext="ext" #test ext field + + + suidRich=BF.suidRich() + entity_list=[] + entity_list.append(entity) + entity_list.append(entity2) + suidRich.insert_batch(entity_list) + + List = suidRich.select(Entity()) + for one in List: + print(one) + + # reset + HoneyConfig.naming_translate_type=old_naming_translate_type + + diff --git a/tests/exam16/full_type4.py b/tests/exam16/full_type4.py new file mode 100644 index 0000000..08b6ff4 --- /dev/null +++ b/tests/exam16/full_type4.py @@ -0,0 +1,78 @@ +import MyConfig +from bee.config import HoneyConfig +from bee.api import SuidRich +import datetime as dt +from entity.full import Entity + + +#批量插入 +if __name__ == '__main__': + + print("start") + + MyConfig.init() + + old_naming_translate_type= HoneyConfig.naming_translate_type + HoneyConfig.naming_translate_type=3 + + + myTime = dt.time(12,34,59)#dt.time(时、分、秒) + + entity=Entity() + entity.id=1 + entity.name="full-type-test" + entity.name2="name2" + entity.flag=False + # entity.flag=True + entity.price=3.12 + entity.descstr="desc" + entity.remark="remark" + entity.modify_date=dt.datetime.today() + entity.updated_time=myTime #sqlite不支持time + entity.created_at=dt.datetime.today() + entity.ext="ext" #test ext field + + entity.map={'name':"John","age":12} + entity.list0=[1, 2, 3,'a', 4, 5 ] + entity.tuple0=(1, 2, 3,'b', 4, '5') + entity.set0="" + + entity.map1={'name':"John","age":12} + entity.list1=[1, 2, 3,'a', 4, 5 ] + entity.tuple1=(1, 2, 3,'b', 4, '5') + entity.set1={1, 2, 'c', 4, 3, '5'} + + entity2=Entity() + entity2.id=2 + entity2.name="full-type-test" + entity2.name2="name2" + # entity2.flag=False + entity2.flag=True + entity2.price=3.12 + entity2.descstr="desc" + entity2.remark="remark" + entity2.modify_date=dt.datetime.today() + entity2.updated_time=myTime #sqlite不支持time + entity2.created_at=dt.datetime.today() + entity2.ext="ext" #test ext field + + + suidRich=SuidRich() + entity_list=[] + entity_list.append(entity) + entity_list.append(entity2) + suidRich.insert_batch(entity_list) + + List = suidRich.select(Entity()) + for one in List: + print(one) + + # reset + HoneyConfig.naming_translate_type=old_naming_translate_type + + print("--------full_type4---------,HoneyConfig.cache_donot_put_cache_result_min_size :", HoneyConfig.cache_donot_put_cache_result_min_size) + print("--------full_type4---------,HoneyConfig.naming_to_lower_before :", HoneyConfig.naming_to_lower_before) + print("--------full_type4---------,HoneyConfig.show_sql_spent_time :", HoneyConfig.show_sql_spent_time) + + + diff --git a/tests/exam16/genBeanTest.py b/tests/exam16/genBeanTest.py new file mode 100644 index 0000000..d3d5a9a --- /dev/null +++ b/tests/exam16/genBeanTest.py @@ -0,0 +1,26 @@ +# from bee.config import HoneyConfig +# from bee.osql.const import DatabaseConst + +import MyConfig +from bee.gen import GenBean + + +if __name__ == '__main__': + print("start") + + MyConfig.init() + + gen = GenBean() + # gen.gen_bean("test_name") + + path="E:\\xxx\\BeePy\\tests" + gen.gen_and_write_bean("test_name", path) + + # #test custom add fetch_bean_sql + # sql="SELECT column_name col, data_type type, nullable ynNull, data_default defaultValue, data_length strLen FROM user_tab_columns WHERE table_name = UPPER('{table_name}') ORDER BY column_id" + # GenBean.add_fetch_bean_sql(DatabaseConst.ORACLE, sql) + # HoneyConfig().set_dbname(DatabaseConst.ORACLE) + # custom_sql=gen._get_fetch_bean_sql("abc") + # print(custom_sql) + + print("finished") diff --git a/tests/exam16/select_someFields_paging.py b/tests/exam16/select_someFields_paging.py new file mode 100644 index 0000000..2abea75 --- /dev/null +++ b/tests/exam16/select_someFields_paging.py @@ -0,0 +1,34 @@ + +import MyConfig +from bee.honeyfactory import BF +from entity.Orders import Orders + + +if __name__ == '__main__': + print("start") + + MyConfig.init() + + orders=Orders() + orders.name = "bee" + + suidRich = BF.suidRich() + # orderList = suidRich.select(orders) + orderList = suidRich.select_paging(orders, 0, 2, "id", "name") + # orderList = suidRich.select_paging(orders, 0, 2, "id", "--name") + # orderList = suidRich.select_paging(orders, 1, 0, "id", "name") + # orderList = suidRich.select_paging(orders, "", 2, "id", "name") + # orderList = suidRich.select_paging(orders,0,2) + + + + # condition=BF.condition() + # condition.selectField("id","name") + # condition.start(1).size(2) + # orderList=suidRich.select(orders, condition) + for one in orderList: + print(one) + + + + print("finished") diff --git a/tests/resources/bee.json b/tests/resources/bee.json index 1022fd1..d48523a 100644 --- a/tests/resources/bee.json +++ b/tests/resources/bee.json @@ -1,5 +1,5 @@ { - "dbName": "SQLite", + "dbname": "SQLite", "database": "bee.db", "dbModuleName":"sqlite3" } \ No newline at end of file diff --git a/tests/resources/bee.properties b/tests/resources/bee.properties index 25f2a89..630607e 100644 --- a/tests/resources/bee.properties +++ b/tests/resources/bee.properties @@ -1,21 +1,34 @@ - - # 'dbName':'MySQL', - # 'host': 'localhost', # \u6570\u636E\u5E93\u4E3B\u673A - # 'user': 'root', # \u66FF\u6362\u4E3A\u60A8\u7684 MySQL \u7528\u6237\u540D - # 'password': '', # \u66FF\u6362\u4E3A\u60A8\u7684 MySQL \u5BC6\u7801 - # 'database': 'bee', # \u66FF\u6362\u4E3A\u60A8\u7684\u6570\u636E\u5E93\u540D\u79F0 - # 'port':3306 - -#value is: MySql,SQLite,Oracle, +#value is: MySql,SQLite,Oracle,... #MySQL config -#bee.db.dbName=MySQL -#bee.db.host =localhost -#bee.db.user =root -#bee.db.password = -#bee.db.database =bee -#bee.db.port=3306 +bee.db.dbname=MySQL +bee.db.host =localhost +bee.db.user =root +bee.db.password = +bee.db.database =bee +bee.db.port=3306 # SQLite -bee.db.dbName=SQLite -bee.db.database =bee.db +#bee.db.dbname=SQLite +#bee.db.database =bee.db + + +bee.show_sql = True +bee.show_sql_params = True +#bee.show_sql_spent_time = True +#bee.show_sql_spent_time_min_ms=2 + +# (DB<-->Java), +# 1: order_no<-->orderNo +# 2: ORDER_NO<-->orderNo +# 3: original, +# 4: ORDER_NO<-->order_no (DbUpperAndJavaLower) +#bee.naming_translate_type = 3 +#bee.naming_to_lower_before=True + +bee.cache_max_size = 1000 +bee.cache_start_delete_rate = 0.6 +bee.cache_full_used_rate = 0.9 +bee.cache_full_clear_rate = 0.2 +bee.cache_key_use_md5 = true +bee.cache_donot_put_cache_result_max_size=500 -- Gitee