diff --git a/bee/__init__.py b/bee/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..20a71278b9c8d0b46aac4eaf06758c883f551c69 --- /dev/null +++ b/bee/__init__.py @@ -0,0 +1,17 @@ +""" +Corresponding Java type ORM tool, +JAVA ORM Bee: + + maven: + + org.teasoft + bee-all + 2.4.2 + + + Gradle(Short): + implementation 'org.teasoft:bee-all:2.4.2' + + note:the version can change to newest. + +""" diff --git a/bee/api.py b/bee/api.py new file mode 100644 index 0000000000000000000000000000000000000000..b20e5df7c501c5d736a51d65cb0328cfd925cb1c --- /dev/null +++ b/bee/api.py @@ -0,0 +1,102 @@ +from bee.obj2sql import ObjToSQL +from bee.osql.logger import Logger +from bee.sqllib import BeeSql + + +class Suid: + + def __init__(self): + self._beeSql = None + self._objToSQL = None + + def select(self, entity): + if entity is None: + return None + + try: + sql,params = self.objToSQL.toSelectSQL(entity) + # Logger.log_sql("select SQL: ", sql) + Logger.logsql("sql: ", sql) + Logger.logsql("params: ",params) + return self.beeSql.select(sql, self.to_class_t(entity),params) # 返回值用到泛型 + finally: + pass # 在 Python 中可以省略 finally, 如果没有清理操作 + + + def select_paging(self, entity, start, size): + if entity is None: + return None + + try: + sql, params = self.objToSQL.toSelectSQLWithPaging(entity,start, size) + # Logger.log_sql("select SQL: ", sql) + Logger.logsql("sql: ", sql) + Logger.logsql("params: ",params) + return self.beeSql.select(sql, self.to_class_t(entity), params) # 返回值用到泛型 + finally: + pass + + def update(self, entity): + if entity is None: + return None + + try: + sql, params = self.objToSQL.toUpdateSQL(entity) + Logger.logsql("sql: ", sql) + Logger.logsql("params: ",params) + return self.beeSql.modify(sql, params) + finally: + pass + + def insert(self, entity): + if entity is None: + return None + + try: + sql, params = self.objToSQL.toInsertSQL(entity) + Logger.logsql("sql: ", sql) + Logger.logsql("params: ",params) + return self.beeSql.modify(sql, params) + finally: + pass + + def delete(self, entity): + if entity is None: + return None + + try: + sql, params = self.objToSQL.toDeleteSQL(entity) + Logger.logsql("sql: ", sql) + Logger.logsql("params: ",params) + return self.beeSql.modify(sql, params) + finally: + pass + + def to_class_t(self, entity): + return type(entity) #返回实体的类型 + + # def __init__(self, beeSql=None, objToSQL=None): + # self._beeSql = beeSql + # self._objToSQL = objToSQL + + @property + def beeSql(self): + if self._beeSql is None: + # self._beeSql = BeeFactory.get_honey_factory().get_beeSql() + self._beeSql =BeeSql() + return self._beeSql + + @beeSql.setter + def beeSql(self, beeSql): + self._beeSql = beeSql + + @property + def objToSQL(self): + if self._objToSQL is None: + self._objToSQL= ObjToSQL() + return self._objToSQL + + @objToSQL.setter + def objToSQL(self, objToSQL): + self._objToSQL = objToSQL + diff --git a/bee/config.py b/bee/config.py new file mode 100644 index 0000000000000000000000000000000000000000..ab811c6b5f22447d2d5385249e303676eb756e24 --- /dev/null +++ b/bee/config.py @@ -0,0 +1,131 @@ +import json + +from bee.key import Key +from bee.osql.logger import Logger + + +class HoneyConfig: + + dbName =None + host =None + user =None + password=None + database=None + port =None + + _loaded = False # 标记是否已加载配置 + + __db_config_data=None + + __instance = None + + def __new__(cls): + if cls.__instance is None: + Logger.debug("HoneyConfig.__new__") + cls.__instance = super().__new__(cls) + cls.__loadConfigInProperties(cls) + cls.__loadConfigInJson(cls) + if cls.port is not None: + cls.port=int(cls.port) + + if cls.__db_config_data is None: + Logger.info("Default loading and init configuration file failed!") + return cls.__instance + + + @staticmethod + def __loadConfigInProperties(cls): + if cls._loaded: + return + config_file = Key.configPropertiesFileName # 文件路径 + + try: + with open(config_file, 'r') as file: + cls._loaded = True # 设置为已加载 + print("Loading config file: "+config_file) + for line in file: + line = line.strip() + # 跳过空行和注释 + if not line or line.startswith('#'): + continue + # 拆分键值对 + try: + key, value = line.split('=',1) + key = key.strip() + value = value.strip() + except ValueError as err: + print(err,line) + continue + + # 检查键是否以 'bee.db.' 开头 + if key.startswith('bee.db.'): + # 获取属性名称 + attr_name = key[len('bee.db.'):] + # 将值赋给对应的属性 + if hasattr(cls, attr_name): + setattr(cls, attr_name, value) + + + cls.__db_config_data = cls.__instance.get_db_config_dict() + except OSError as err: + print(err) + + + @staticmethod + def __loadConfigInJson(cls): + if not cls._loaded: #只加载一次 + # config_file_path = os.path.join(os.path.dirname(__file__), 'config.json') + config_file=Key.configJsonFileName + print("Loading config file: "+config_file) + try: + with open(config_file, 'r') as config_file: + cls._loaded = True # 设置为已加载 + cls.__db_config_data = json.load(config_file) + + cls.dbName=cls.__db_config_data.get("dbName") + + except OSError as err: + print(err) + + + def get_db_config_dict(self): + """将DB相关的类属性打包成字典并返回""" + cls=type(self) + if cls.__db_config_data is not None: + return cls.__db_config_data + + cls.__db_config_data={} + + if HoneyConfig.dbName is not None: + cls.__db_config_data['dbName'] = HoneyConfig.dbName + if HoneyConfig.host is not None: + cls.__db_config_data['host'] = HoneyConfig.host + if HoneyConfig.user is not None: + cls.__db_config_data['user'] = HoneyConfig.user + if HoneyConfig.password is not None: + cls.__db_config_data['password'] = HoneyConfig.password + if HoneyConfig.database is not None: + cls.__db_config_data['database'] = HoneyConfig.database + if HoneyConfig.port is not None: + cls.__db_config_data['port'] = int(HoneyConfig.port) + + return cls.__db_config_data + + def set_db_config_dict(self,config): + cls=type(self) + cls.__db_config_data=config + + if config is not None: + Logger.info("Reset db_config_data") + if config.get("dbName") is not None: + if cls.__db_config_data is None: + cls.__db_config_data={} + cls.__db_config_data["dbName"] = config.get("dbName") + + def get_dbName(self): + return HoneyConfig.dbName.lower() + + def set_dbName(self, dbName): + print("---------------:"+dbName) + HoneyConfig.dbName = dbName + diff --git a/bee/conn_builder.py b/bee/conn_builder.py new file mode 100644 index 0000000000000000000000000000000000000000..7c3b5a2efc2df4d06ef06d6a6c8cc80f3ba4497d --- /dev/null +++ b/bee/conn_builder.py @@ -0,0 +1,88 @@ +# import sqlite3 +# import pymysql +import importlib + +from bee.key import Key +from bee.osql.const import DatabaseConst +from bee.osql.logger import Logger + +class ConnectionBuilder: + + @staticmethod + def build_connect(config): + dbName = None + if Key.dbName in config: + tempConfig = config.copy() + dbName = tempConfig.pop(Key.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', + } + + # 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!") + return None + + dbName = dbName.lower() + + if Key.dbModuleName in config: #优先使用dbModuleName,让用户可以有选择覆盖默认配置的机会 + dbModuleName = tempConfig.pop(Key.dbModuleName, None) + elif dbName not in db_modules: + # raise ValueError(f"Database type '{dbName}' is not supported, need config dbModuleName.") + print(f"Database type '{dbName}' is not supported, need config dbModuleName.") + return None + else: + dbModuleName = db_modules[dbName] + + + db_module = importlib.import_module(dbModuleName) + Logger.info(f"Use {dbModuleName}!") + + # Now create the connection using the imported module + if dbName == DatabaseConst.MYSQL.lower(): + return db_module.connect(**tempConfig) + elif dbName == DatabaseConst.SQLite.lower(): + return db_module.connect(**tempConfig) + + + + +# ### 2. 使用 `psycopg2`(PostgreSQL) +# +# ```python +# import psycopg2 +# +# def fetch_user_data_postgresql(username, age): +# connection = psycopg2.connect( +# host='localhost', +# user='your_username', +# password='your_password', +# database='your_database' +# ) + + + + +# import cx_Oracle +# +# def fetch_user_data_oracle(username, age): +# connection = cx_Oracle.connect('username/password@localhost/orcl') + +# Or +# import cx_Oracle +# +# # 创建数据库连接 +# def create_connection(): +# dsn = cx_Oracle.makedsn("hostname", 1521, service_name="your_service_name") +# connection = cx_Oracle.connect(user="your_username", password="your_password", dsn=dsn) +# return connection diff --git a/bee/context.py b/bee/context.py new file mode 100644 index 0000000000000000000000000000000000000000..2ef0f810c56f8954b2bd896eb18773244c7e77e9 --- /dev/null +++ b/bee/context.py @@ -0,0 +1,55 @@ +# import sqlite3 +# import pymysql +from bee.config import HoneyConfig +from bee.conn_builder import ConnectionBuilder +from bee.factory import BeeFactory +from bee.key import Key +from bee.osql.const import DatabaseConst + + +class HoneyContext: + + dbName=None + + @staticmethod + def get_connection(): + + factory = BeeFactory() + conn = factory.get_connection() + if conn is not None: + # conn.ping(reconnect=True) #重新获取,要重连。 只是mysql? + return conn + + honeyConfig = HoneyConfig() + config = honeyConfig.get_db_config_dict() + + HoneyContext.__setDbName(config) + conn = ConnectionBuilder.build_connect(config) + factory.set_connection(conn) + return conn + + @staticmethod + def __setDbName(config): + if Key.dbName in config: + dbName = config.get(Key.dbName, None) + if dbName is not None: + HoneyContext.dbName = dbName + + @staticmethod + def get_placeholder(): + + honeyConfig=HoneyConfig() + dbName=honeyConfig.get_dbName().lower() + + if dbName is None: + return None + elif dbName == DatabaseConst.MYSQL.lower() or dbName == DatabaseConst.PostgreSQL.lower(): + return "%s" + elif dbName == DatabaseConst.SQLite.lower(): + return "?" + elif dbName == DatabaseConst.ORACLE.lower(): + # query = "SELECT * FROM users WHERE username = :username AND age = :age" + return ":" + + + #还要有set的方法,或在配置文件中设置 TODO diff --git a/bee/factory.py b/bee/factory.py new file mode 100644 index 0000000000000000000000000000000000000000..0cfa69f62428715fac4eb49ad7d9f2dd64e91f4c --- /dev/null +++ b/bee/factory.py @@ -0,0 +1,20 @@ +class BeeFactory: + + __connection = None + + __instance = None + + def __new__(cls): + if cls.__instance is None: + cls.__instance = super().__new__(cls) + return cls.__instance + + def set_connection(self, connection): + BeeFactory.__connection = connection + + def get_connection(self): + return BeeFactory.__connection + + # def __getattribute__(self, item): + # print(f"Accessing attribute: {item}") + # return super().__getattribute__(item) \ No newline at end of file diff --git a/bee/key.py b/bee/key.py new file mode 100644 index 0000000000000000000000000000000000000000..035a5759ee99acb594adf820d824fd1bed106acf --- /dev/null +++ b/bee/key.py @@ -0,0 +1,13 @@ + + +class Key: + tablename = "__tablename__" + pk = "__pk__" + primary_key = "__primary_key__" + id="id" + + dbModuleName="dbModuleName" + dbName="dbName" + + configPropertiesFileName="bee.properties" + configJsonFileName="bee.json" \ No newline at end of file diff --git a/bee/name/NameUtil.py b/bee/name/NameUtil.py new file mode 100644 index 0000000000000000000000000000000000000000..04bff41460dd6b923364949e8f4d4b31f6b942a4 --- /dev/null +++ b/bee/name/NameUtil.py @@ -0,0 +1,22 @@ +# NameUtil + + +def getClassName(obj): + pass + + +def toUnderscoreNaming(name): + pass + + +def toCamelNaming(name): + pass + + +def firstLetterToUpperCase(name): + pass + + +def firstLetterToLowerCase(name): + pass + diff --git a/bee/name/UnderScoreAndCamelName.py b/bee/name/UnderScoreAndCamelName.py new file mode 100644 index 0000000000000000000000000000000000000000..0ab9f726c200e46b967515f192610b37831ec9dc --- /dev/null +++ b/bee/name/UnderScoreAndCamelName.py @@ -0,0 +1,23 @@ +from bee.name import NameUtil + + +def toTableName(entityName): + return NameUtil.toUnderscoreNaming(NameUtil.firstLetterToLowerCase(entityName)) + + +def toColumnName(fieldName): + return NameUtil.toUnderscoreNaming(fieldName) + + +# def toEntityName(tableName): +# if (HoneyConfig.getHoneyConfig().naming_toLowerCaseBefore): +# # need lowercase first if the name has upper case +# tableName = tableName.toLowerCase() +# return NameUtil.firstLetterToUpperCase(NameUtil.toCamelNaming(tableName)) +# +# +# def toFieldName(columnName): +# if (HoneyConfig.getHoneyConfig().naming_toLowerCaseBefore) : +# # need lowercase first if the name has upper case +# columnName = columnName.toLowerCase(); # if not , BEE_NAME -> BEENAME -> ?? +# return NameUtil.toCamelNaming(columnName) diff --git a/bee/name/__init__.py b/bee/name/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/bee/obj2sql.py b/bee/obj2sql.py new file mode 100644 index 0000000000000000000000000000000000000000..3fca8b91f92f1b5f1e7f93c975be5322de233557 --- /dev/null +++ b/bee/obj2sql.py @@ -0,0 +1,224 @@ +from bee.config import HoneyConfig +from bee.context import HoneyContext +from bee.key import Key +from bee.osql.const import DatabaseConst +from bee.paging import Paging +from bee.util import HoneyUtil + + +class ObjToSQL: + + # def toSelectSQL(self, entity): + # cls=type(entity) + # classField = HoneyUtil.get_class_field(cls) # list + # fieldAndValue = HoneyUtil.get_obj_field_value(entity) # dict + # + # classFieldAndValue = HoneyUtil.get_class_field_value(cls) + # + # table_name=HoneyUtil.get_table_name(entity) + # if not fieldAndValue: + # return f"SELECT {', '.join(classField)} FROM {table_name}", None + # + # # objKey = fieldAndValue.keys() + # # 获取去掉前缀的键 + # # objKey = [key.lstrip('_') for key in fieldAndValue.keys()] + # fieldAndValue = {key.lstrip('_'): value for key, value in fieldAndValue.items()} + # objKey = fieldAndValue.keys() + # set1 = set(classField) + # set2 = set(objKey) # list转set 顺序会乱了 + # setExt = set2 - set1 + # + # # 默认删除动态加的属性 + # for k in setExt: + # fieldAndValue.pop(k, None) + # + # #若对象的属性的值是None,则使用类级别的 + # for name, value in fieldAndValue.items(): + # if value is None: + # fieldAndValue[name]=classFieldAndValue[name] + # + # print(fieldAndValue) + # + # # 提取条件的键值对 + # condition_list = [] + # value_list=[] + # ph=self.__getPlaceholder() + # for key, value in fieldAndValue.items(): + # if value is not None: + # condition_list.append(f"{key} = {ph}") + # value_list.append(value) + # + # where_clause = " AND ".join(condition_list) + # if where_clause is not None and where_clause != '' : + # sql = f"SELECT {', '.join(classField)} FROM {table_name} WHERE {where_clause}" + # else: + # sql = f"SELECT {', '.join(classField)} FROM {table_name}" + # + # return sql, value_list #如何将值放入上下文 TODO + + + + + def toSelectSQLWithPaging(self, entity, start, size): + sql, params = self.toSelectSQL(entity) + + paging = Paging() + sql = paging.to_page_sql(sql, start, size) + return sql, params + + def toSelectSQL(self, entity): + fieldAndValue, classField = self.__getKeyValue_classField(entity) + + table_name = HoneyUtil.get_table_name(entity) + return self.__build_select_sql(table_name, classField, fieldAndValue); + + def toUpdateSQL(self, entity): + fieldAndValue = self.__getKeyValue(entity) + pk = HoneyUtil.get_pk(entity) + if pk is None: + if not Key.id in fieldAndValue: + # if not "id" in fieldAndValue: + print("update by id,bean has id field or need set the pk field name with __pk__") # TODO throw exception + else: + idvalue = fieldAndValue.get(Key.id, None) + if idvalue is None: + print("the id value can not be None") + else: + pk = Key.id + + conditions = {pk:fieldAndValue.pop(pk)} + + table_name = HoneyUtil.get_table_name(entity) + return self.__build_update_sql(table_name, fieldAndValue, conditions) + + + def toInsertSQL(self, entity): + table_name = HoneyUtil.get_table_name(entity) + 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) + return self.__build_delete_sql(table_name, fieldAndValue) + + def __getKeyValue(self, entity): + fieldAndValue, _ = self.__getKeyValue_classField(entity) + return fieldAndValue + + #__ or _只是用于范围保护,转成sql时,将这两种前缀统一去掉 + def __getKeyValue_classField(self, entity): + + cls=type(entity) + classField = HoneyUtil.get_class_field(cls) # list + fieldAndValue = HoneyUtil.get_obj_field_value(entity) # dict + # print(fieldAndValue) + + classFieldAndValue = HoneyUtil.get_class_field_value(cls) + # print("aaaaa------------") + # print(classFieldAndValue) + + # 获取去掉前缀的键 TODO __ ?? + # fieldAndValue = {key.lstrip('_'): value for key, value in fieldAndValue.items()} + + fieldAndValue = HoneyUtil.remove_prefix(fieldAndValue) + + + objKey = fieldAndValue.keys() + + set1 = set(classField) + set2 = set(objKey) # list转set 顺序会乱了 + + setExt = set2 - set1 + + # 默认删除动态加的属性 + for k in setExt: + fieldAndValue.pop(k, None) + + # 若对象的属性的值是None,则使用类级别的 + for name, value in fieldAndValue.items(): + if value is None: + fieldAndValue[name] = classFieldAndValue[name] + + # 当对象的属性没有相应的值,而类的属性有,则使用类级的属性 + for name, value in classFieldAndValue.items(): + if value is not None and fieldAndValue.get(name, None) is None: + fieldAndValue[name] = value + + #排除value为None的项 + fieldAndValue = {key: value for key, value in fieldAndValue.items() if value is not None} + + return fieldAndValue, classField + + def __getPlaceholder(self): + return HoneyContext.get_placeholder() + + + def __build_update_sql(self, table_name, set_dict, conditions): + ph=self.__getPlaceholder() + if self.__getPlaceholderType() == 3: + updateSet = ', '.join(f"{key} = {ph}{key}" for key in set_dict.keys()) + condition_str = " AND ".join(f"{key} = {ph}{key}" for key in conditions.keys()) + else: + updateSet = ', '.join(f"{key} = {ph}" for key in set_dict.keys()) + condition_str = " AND ".join(f"{key} = {ph}" for key in conditions.keys()) + + sql = f"UPDATE {table_name} SET {updateSet} WHERE {condition_str}" + params = list(set_dict.values()) + list(conditions.values()) + return sql, params + + + def __build_insert_sql(self, table_name, data): + ph=self.__getPlaceholder() + columns = ', '.join(data.keys()) + if self.__getPlaceholderType() == 3: + placeholders = ', '.join(f" {ph}{key}" for key in data.keys()) + else: + placeholders = ', '.join(f"{ph}" for _ in data) + sql = f"INSERT INTO {table_name} ({columns}) VALUES ({placeholders})" + return sql, list(data.values()) + + def __build_where_condition(self, conditions): + ph=self.__getPlaceholder() + if self.__getPlaceholderType() == 3: + condition_str = " AND ".join(f"{key} = {ph}{key}" for key in conditions.keys()) + else: + condition_str = " AND ".join(f"{key} = {ph}" for key in conditions.keys()) + return condition_str + + def __build_select_sql(self, table_name, classField, conditions=None): + # sql = f"SELECT * FROM {table_name}" + sql = f"SELECT {', '.join(classField)} FROM {table_name}" + + #where part + params = [] + if conditions: + condition_str=self.__build_where_condition(conditions) + sql += f" WHERE {condition_str}" + params = list(conditions.values()) + return sql, params + + + def __build_delete_sql(self, table_name, conditions): + + sql = f"DELETE FROM {table_name}" + params = [] + if conditions: + condition_str=self.__build_where_condition(conditions) + sql += f" WHERE {condition_str}" + params = list(conditions.values()) + return sql, params + + def __get_dbName(self): + honeyConfig=HoneyConfig() + return honeyConfig.get_dbName() + + def __getPlaceholderType(self): + + # sql = "SELECT * FROM employees WHERE employee_id = :emp_id" + # cursor.execute(sql, emp_id=emp_id) + # sql = "INSERT INTO employees (employee_id, employee_name, salary) VALUES (:emp_id, :emp_name, :emp_salary)" + # cursor.execute(sql, emp_id=emp_id, emp_name=emp_name, emp_salary=emp_salary) + if DatabaseConst.ORACLE.lower() == self.__get_dbName(): + return 3 diff --git a/bee/osql/__init__.py b/bee/osql/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/bee/osql/const.py b/bee/osql/const.py new file mode 100644 index 0000000000000000000000000000000000000000..4a138c358cf6031882ad9e98d2537771a6c41cae --- /dev/null +++ b/bee/osql/const.py @@ -0,0 +1,49 @@ + + +class DatabaseConst: + MYSQL = "MySQL" + MariaDB = "MariaDB" + ORACLE = "Oracle" + SQLSERVER = "Microsoft SQL Server" + MsAccess = "Microsoft Access" # Microsoft Access + AzureSQL = SQLSERVER + + H2 = "H2" + SQLite = "SQLite" + PostgreSQL = "PostgreSQL" + + Cubrid = "Cubrid" + DB2400 = "DB2 UDB for AS/400" + DB2 = "DB2" + Derby = "Apache Derby" + Firebird = "Firebird" + FrontBase = "FrontBase" + + HSQL = "HSQL Database Engine" + HSQLDB = "HSQL Database" + Informix = "Informix Dynamic Server" + Ingres = "Ingres" + JDataStore = "JDataStore" + Mckoi = "Mckoi" + MimerSQL = "MimerSQL" + Pointbase = "Pointbase" + + SAPDB = "SAPDB" + Sybase = "Sybase SQL Server" + Teradata = "Teradata" + TimesTen = "TimesTen" + + DM = "DM DBMS" + Kingbase = "KingbaseES" + GaussDB = "GaussDB" + + OceanBase = "OceanBase" + + # NoSql + Cassandra = "Cassandra" + Hbase = "Hbase" + Hypertable = "Hypertable" + DynamoDB = "DynamoDB" + + MongoDB = "MongoDB" + CouchDB = "CouchDB" diff --git a/bee/osql/logger.py b/bee/osql/logger.py new file mode 100644 index 0000000000000000000000000000000000000000..d0995813944a0efb54d0830bf2411b8ebd4a9c98 --- /dev/null +++ b/bee/osql/logger.py @@ -0,0 +1,24 @@ + + +class Logger: + + @staticmethod + def debug(msg): + print(msg) + + @staticmethod + def info(msg): + print(msg) + + @staticmethod + def warn(msg): + print(msg) + + @staticmethod + def error(msg): + print(msg) + + @staticmethod + def logsql(*msg): + print(*msg) + diff --git a/bee/osql/upperkey.py b/bee/osql/upperkey.py new file mode 100644 index 0000000000000000000000000000000000000000..5750fe1113d1d073adea07b6b1054c6fc85958bb --- /dev/null +++ b/bee/osql/upperkey.py @@ -0,0 +1,5 @@ + + +class UpperKey: + SPACE = " " + SELECT = "SELECT" diff --git a/bee/paging.py b/bee/paging.py new file mode 100644 index 0000000000000000000000000000000000000000..ef9049525cf7df04cf9d092986ee334f599df839 --- /dev/null +++ b/bee/paging.py @@ -0,0 +1,25 @@ +from bee.config import HoneyConfig +from bee.osql.const import DatabaseConst + + +class Paging: + + def to_page_sql(self, sql, start, size): + config = HoneyConfig() + dbName = config.get_dbName() + if dbName is None: + # 使用配置的dbName TODO 抛异常 + return sql + elif dbName == DatabaseConst.MYSQL.lower(): + return self.__toPageSqlForMySql(sql, start, size) + elif dbName == DatabaseConst.SQLite.lower(): + return self.__toLimitOffsetPaging(sql, start, size) + + def __toPageSqlForMySql(self, sql, start, size): + limitStament = " " + "limit" + " " + str(start) + ", " + str(size) + sql += limitStament + return sql + + def __toLimitOffsetPaging(self, sql, offset, size): + return sql + " " + "limit" + " " + str(size) + " " + "offset" + " " + str(offset) + diff --git a/bee/sqllib.py b/bee/sqllib.py new file mode 100644 index 0000000000000000000000000000000000000000..208f5347b30a42bc105754908aed48594092e468 --- /dev/null +++ b/bee/sqllib.py @@ -0,0 +1,59 @@ +from bee.context import HoneyContext +from bee.util import HoneyUtil + +class BeeSql: + + def select(self, sql, entityClass, params=None): + # def select(self, sql: str, entityClass: type, params=None) -> list: + + conn = self.__getConn() + if conn is None: + return None + + rs_list = [] + cursor = conn.cursor() + try: + ## with conn.cursor() as cursor: # SQLite不支持with语法 + # 执行 SQL 查询 + cursor.execute(sql, params or []) + # 获取列名 + column_names = [description[0] for description in cursor.description] + # 获取所有结果 + results = cursor.fetchall() + + for row in results: + # 将行数据映射到新创建的实体对象 + target_obj = HoneyUtil.transform_result(row, column_names, entityClass) + rs_list.append(target_obj) + + except Exception as err: # TODO 异常处理 + print(f"Error: {err}") + finally: + # 清理资源 + if conn is not None: + conn.close() + return rs_list + + + """ 执行 UPDATE/INSERT/DELETE 操作 """ + # def modify(self, sql: str, params=None) -> int: + def modify(self, sql, params=None): + conn = self.__getConn() + if conn is None: + return None # TODO抛异常 + cursor = conn.cursor() + try: + cursor.execute(sql, params or []) + conn.commit() + return cursor.rowcount # 返回受影响的行数 + except Exception as e: + print(f"Error in modify: {e}") + conn.rollback() + return 0 + finally: + if conn is not None: + conn.close() + + def __getConn(self): + return HoneyContext.get_connection() + diff --git a/bee/util.py b/bee/util.py new file mode 100644 index 0000000000000000000000000000000000000000..0679de8fb29def149241b832c8fb6f985be17972 --- /dev/null +++ b/bee/util.py @@ -0,0 +1,135 @@ +from bee.key import Key + + +class HoneyUtil: + + """返回给定对象的属性字典,如果没有则返回None""" + + @staticmethod + def get_obj_field_value(obj): + if hasattr(obj, '__dict__'): + # print(obj.__dict__) + return obj.__dict__ + else: + return None + + @staticmethod + def get_class_field_value(cls): + + if hasattr(cls, '__dict__'): + # 并去掉前缀__和_ 只是__开前开头,会变化的。 + class_name = cls.__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的类级别的值。 TODO 应用使用_或__前缀 + # kv[key]=getattr(cls, key) + 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) } + # else: + # return None + + # if hasattr(cls, '__dict__'): + # return { + # key: (getattr(cls, key)() if isinstance(getattr(cls, key), property) else getattr(cls, key)) + # for key in cls.__dict__.keys() + # if not (key.startswith('__') and key.endswith('__')) # 排除私有属性 + # } + # else: + # return None + + # result = {} + # for key in cls.__class__.__dict__: + # # 排除私有属性 + # if not (key.startswith('__') and key.endswith('__')): + # value = getattr(cls, key) + # # 如果是property类型,则获取其值 + # if isinstance(value, property): + # result[key] = getattr(cls, key) # 通过实例获取属性值 + # else: + # result[key] = value + # print(result) + # return result + # dict: {'id': , 'name': , 'remark': } + + """ 返回给定类的属性字典,但不包括系统的 """ + + @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 + + # 对象的不会改 + """ remove __ or _ prefix """ + @staticmethod + def remove_prefix(dict_obj): + if dict_obj is None: + return dict_obj + + fieldAndValue = { + key[2:] if key.startswith('__') else key[1:] if key.startswith('_') else key: value + # key[1:] if key.startswith('_') else key: value + for key, value in dict_obj.items() + } + return fieldAndValue + + @staticmethod + def get_table_name(obj): + cls = obj.__class__ + # print(cls) + # temp_name=cls.__tablename__ + temp_name = getattr(cls, '__tablename__', None) + if temp_name is not None and not temp_name.isspace(): + return temp_name + class_name = cls.__name__ + table_name = class_name.lower() # 还要应用多种转换规则 TODO + return table_name + + """ get pk from bean""" + + @staticmethod + def get_pk(obj): + cls = obj.__class__ + temp_name = getattr(cls, Key.pk, None) + if temp_name is not None and not temp_name.isspace(): + return temp_name + else: + temp_name = getattr(cls, Key.primary_key, None) + if temp_name is not None and not temp_name.isspace(): + return temp_name + 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 +