springbootAI 1.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- spring/__init__.py +66 -0
- spring/ai/__init__.py +78 -0
- spring/ai/advisors.py +139 -0
- spring/ai/annotations.py +74 -0
- spring/ai/autoconfig.py +481 -0
- spring/ai/core.py +391 -0
- spring/ai/etl.py +188 -0
- spring/ai/memory.py +109 -0
- spring/ai/observability.py +129 -0
- spring/ai/providers.py +789 -0
- spring/ai/resilience.py +258 -0
- spring/ai/tools.py +106 -0
- spring/ai/vectorstore.py +303 -0
- spring/annotations/__init__.py +188 -0
- spring/annotations/cache.py +126 -0
- spring/annotations/cloud.py +207 -0
- spring/annotations/conditional.py +272 -0
- spring/annotations/core.py +864 -0
- spring/annotations/messaging.py +107 -0
- spring/aop/__init__.py +4 -0
- spring/aop/cloud_aop.py +404 -0
- spring/aop/comprehensive_aop.py +1015 -0
- spring/aop/method_interceptor.py +19 -0
- spring/aop/proxy_factory.py +55 -0
- spring/cloud/__init__.py +76 -0
- spring/cloud/discovery.py +364 -0
- spring/cloud/feign.py +469 -0
- spring/cloud/gateway.py +452 -0
- spring/cloud/load_balancer.py +149 -0
- spring/cloud/seata.py +557 -0
- spring/cloud/sentinel.py +525 -0
- spring/cloud/tracer.py +337 -0
- spring/config/__init__.py +21 -0
- spring/config/binding.py +206 -0
- spring/config/config_loader.py +405 -0
- spring/context/__init__.py +13 -0
- spring/context/application_context.py +589 -0
- spring/context/bean_definition.py +70 -0
- spring/context/bean_factory.py +1052 -0
- spring/context/registry.py +58 -0
- spring/context/scanner.py +106 -0
- spring/core/__init__.py +3 -0
- spring/core/graceful_shutdown.py +196 -0
- spring/core/typing_utils.py +50 -0
- spring/csv/__init__.py +52 -0
- spring/csv/annotations.py +402 -0
- spring/csv/converters.py +69 -0
- spring/csv/easy_csv.py +95 -0
- spring/csv/exceptions.py +27 -0
- spring/csv/reader.py +195 -0
- spring/csv/writer.py +155 -0
- spring/data/__init__.py +54 -0
- spring/data/page.py +181 -0
- spring/data/repository.py +274 -0
- spring/data/specification.py +228 -0
- spring/datasource/__init__.py +66 -0
- spring/datasource/annotations.py +133 -0
- spring/datasource/context.py +69 -0
- spring/datasource/dynamic.py +148 -0
- spring/event/__init__.py +7 -0
- spring/event/publisher.py +69 -0
- spring/excel/__init__.py +51 -0
- spring/excel/annotations.py +405 -0
- spring/excel/converters.py +231 -0
- spring/excel/easy_excel.py +94 -0
- spring/excel/exceptions.py +31 -0
- spring/excel/reader.py +254 -0
- spring/excel/style.py +95 -0
- spring/excel/writer.py +197 -0
- spring/i18n/__init__.py +97 -0
- spring/i18n/accessor.py +94 -0
- spring/i18n/auto_config.py +177 -0
- spring/i18n/holder.py +106 -0
- spring/i18n/locale.py +152 -0
- spring/i18n/locale_resolver.py +367 -0
- spring/i18n/message_source.py +250 -0
- spring/i18n/middleware.py +79 -0
- spring/i18n/properties.py +168 -0
- spring/i18n/sources.py +255 -0
- spring/logging/__init__.py +1 -0
- spring/logging/loguru_logger.py +228 -0
- spring/main.py +378 -0
- spring/messaging/__init__.py +1 -0
- spring/messaging/rabbitmq.py +302 -0
- spring/monitoring/__init__.py +1 -0
- spring/monitoring/prometheus.py +199 -0
- spring/orm/__init__.py +258 -0
- spring/orm/database.py +222 -0
- spring/orm/ddl_auto.py +1217 -0
- spring/orm/migration.py +419 -0
- spring/orm/mybatis_integration.py +400 -0
- spring/orm/pymybatis/__init__.py +86 -0
- spring/orm/pymybatis/annotations/__init__.py +30 -0
- spring/orm/pymybatis/annotations/annotations.py +332 -0
- spring/orm/pymybatis/cache/__init__.py +47 -0
- spring/orm/pymybatis/cache/cache.py +371 -0
- spring/orm/pymybatis/cache/redis_cache.py +434 -0
- spring/orm/pymybatis/circuit_breaker/__init__.py +21 -0
- spring/orm/pymybatis/circuit_breaker/circuit_breaker.py +424 -0
- spring/orm/pymybatis/configuration.py +525 -0
- spring/orm/pymybatis/core/__init__.py +10 -0
- spring/orm/pymybatis/core/sql_session.py +1382 -0
- spring/orm/pymybatis/core/sql_session_factory.py +76 -0
- spring/orm/pymybatis/dialect/__init__.py +9 -0
- spring/orm/pymybatis/dialect/dialect.py +445 -0
- spring/orm/pymybatis/dynamic_sql/__init__.py +9 -0
- spring/orm/pymybatis/dynamic_sql/dynamic_sql.py +900 -0
- spring/orm/pymybatis/interceptor/__init__.py +31 -0
- spring/orm/pymybatis/interceptor/interceptor.py +427 -0
- spring/orm/pymybatis/mapper/__init__.py +9 -0
- spring/orm/pymybatis/mapper/mapper.py +540 -0
- spring/orm/pymybatis/metrics/__init__.py +41 -0
- spring/orm/pymybatis/metrics/metrics.py +595 -0
- spring/orm/pymybatis/pool/__init__.py +9 -0
- spring/orm/pymybatis/pool/connection_pool.py +711 -0
- spring/orm/pymybatis/security/__init__.py +19 -0
- spring/orm/pymybatis/security/access_control.py +415 -0
- spring/orm/pymybatis/security/password_encoder.py +293 -0
- spring/orm/pymybatis/security/sensitive_data_masker.py +326 -0
- spring/orm/pymybatis/security/sql_injection_detector.py +675 -0
- spring/orm/pymybatis/transaction/__init__.py +9 -0
- spring/orm/pymybatis/transaction/transaction.py +288 -0
- spring/orm/pymybatis/type_handler/__init__.py +37 -0
- spring/orm/pymybatis/type_handler/type_handler.py +473 -0
- spring/orm/pymybatis/version.py +9 -0
- spring/orm/pymybatis/xml_parser/__init__.py +9 -0
- spring/orm/pymybatis/xml_parser/xml_parser.py +761 -0
- spring/retry/__init__.py +12 -0
- spring/retry/retry_annotations.py +71 -0
- spring/retry/retry_decorator.py +155 -0
- spring/scheduling/__init__.py +3 -0
- spring/scheduling/scheduler.py +389 -0
- spring/security/__init__.py +39 -0
- spring/security/jwt_utils.py +281 -0
- spring/security/replay_protection.py +206 -0
- spring/security/secret_manager.py +226 -0
- spring/security/security_aop.py +248 -0
- spring/security/security_context.py +172 -0
- spring/test/__init__.py +45 -0
- spring/test/slicing.py +341 -0
- spring/tracing/__init__.py +11 -0
- spring/tracing/skywalking.py +229 -0
- spring/tx/__init__.py +52 -0
- spring/tx/events.py +172 -0
- spring/tx/synchronization.py +143 -0
- spring/utils/__init__.py +5 -0
- spring/utils/banner.py +32 -0
- spring/utils/logger.py +73 -0
- spring/utils/redis_client.py +526 -0
- spring/validation/__init__.py +55 -0
- spring/validation/aop.py +141 -0
- spring/validation/constraints.py +357 -0
- spring/validation/exceptions.py +55 -0
- spring/validation/validator.py +139 -0
- spring/web/__init__.py +12 -0
- spring/web/actuator.py +319 -0
- spring/web/exception_handler.py +61 -0
- spring/web/health.py +399 -0
- spring/web/interceptor.py +91 -0
- spring/web/result.py +44 -0
- spring/web/swagger.py +601 -0
- spring/web/web_context.py +755 -0
- spring/websocket/__init__.py +86 -0
- spring/websocket/annotations.py +169 -0
- spring/websocket/broker.py +238 -0
- spring/websocket/exceptions.py +26 -0
- spring/websocket/handler.py +243 -0
- spring/websocket/router.py +526 -0
- spring/websocket/session.py +216 -0
- springbootai-1.8.0.dist-info/METADATA +2796 -0
- springbootai-1.8.0.dist-info/RECORD +175 -0
- springbootai-1.8.0.dist-info/WHEEL +5 -0
- springbootai-1.8.0.dist-info/entry_points.txt +2 -0
- springbootai-1.8.0.dist-info/licenses/LICENSE +7 -0
- springbootai-1.8.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyMyBatis SqlSessionFactory模块
|
|
3
|
+
|
|
4
|
+
SqlSessionFactory是SqlSession的工厂类,负责创建SqlSession实例
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional
|
|
8
|
+
from ..configuration import Configuration
|
|
9
|
+
from .sql_session import SqlSession, create_pool_from_configuration
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SqlSessionFactory:
|
|
13
|
+
"""
|
|
14
|
+
SqlSession工厂类
|
|
15
|
+
|
|
16
|
+
核心功能:
|
|
17
|
+
1. 创建SqlSession实例
|
|
18
|
+
2. 管理配置
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, configuration: Optional[Configuration] = None):
|
|
22
|
+
"""
|
|
23
|
+
初始化SqlSessionFactory
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
configuration: 配置对象,不指定则使用默认配置
|
|
27
|
+
"""
|
|
28
|
+
self.configuration = configuration or Configuration()
|
|
29
|
+
self.connection_pool = create_pool_from_configuration(self.configuration)
|
|
30
|
+
self._closed = False
|
|
31
|
+
|
|
32
|
+
def open_session(self) -> SqlSession:
|
|
33
|
+
"""
|
|
34
|
+
创建SqlSession实例
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
SqlSession实例
|
|
38
|
+
"""
|
|
39
|
+
if self._closed:
|
|
40
|
+
raise RuntimeError("SqlSessionFactory 已关闭")
|
|
41
|
+
return SqlSession(self.configuration, connection_pool=self.connection_pool)
|
|
42
|
+
|
|
43
|
+
def get_configuration(self) -> Configuration:
|
|
44
|
+
"""
|
|
45
|
+
获取配置对象
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
配置对象
|
|
49
|
+
"""
|
|
50
|
+
return self.configuration
|
|
51
|
+
|
|
52
|
+
def set_configuration(self, configuration: Configuration) -> None:
|
|
53
|
+
"""
|
|
54
|
+
设置配置对象
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
configuration: 配置对象
|
|
58
|
+
"""
|
|
59
|
+
self.connection_pool.close()
|
|
60
|
+
self.configuration = configuration
|
|
61
|
+
self.connection_pool = create_pool_from_configuration(configuration)
|
|
62
|
+
self._closed = False
|
|
63
|
+
|
|
64
|
+
def close(self) -> None:
|
|
65
|
+
"""关闭工厂拥有的共享连接池。"""
|
|
66
|
+
if self._closed:
|
|
67
|
+
return
|
|
68
|
+
self.connection_pool.close()
|
|
69
|
+
self._closed = True
|
|
70
|
+
|
|
71
|
+
def __enter__(self):
|
|
72
|
+
return self
|
|
73
|
+
|
|
74
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
75
|
+
self.close()
|
|
76
|
+
return False
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyMyBatis数据库方言模块
|
|
3
|
+
|
|
4
|
+
支持MySQL、PostgreSQL、SQLite、Oracle等数据库的SQL方言适配
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .dialect import Dialect, MySQLDialect, PostgreSQLDialect, SQLiteDialect, OracleDialect, get_dialect
|
|
8
|
+
|
|
9
|
+
__all__ = ['Dialect', 'MySQLDialect', 'PostgreSQLDialect', 'SQLiteDialect', 'OracleDialect', 'get_dialect']
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyMyBatis数据库方言模块
|
|
3
|
+
|
|
4
|
+
定义各数据库的SQL语法差异,实现SQL方言适配
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from typing import Optional, List, Tuple
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Dialect(ABC):
|
|
12
|
+
"""
|
|
13
|
+
数据库方言抽象基类
|
|
14
|
+
|
|
15
|
+
定义各数据库特有的SQL语法:
|
|
16
|
+
- 分页查询
|
|
17
|
+
- 主键自增
|
|
18
|
+
- 批量插入
|
|
19
|
+
- 特殊函数
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def get_dialect_name(self) -> str:
|
|
24
|
+
"""获取方言名称"""
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def get_pagination_sql(self, sql: str, offset: int, limit: int) -> str:
|
|
29
|
+
"""
|
|
30
|
+
获取分页SQL
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
sql: 原始SQL
|
|
34
|
+
offset: 偏移量
|
|
35
|
+
limit: 每页条数
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
分页SQL
|
|
39
|
+
"""
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
@abstractmethod
|
|
43
|
+
def get_insert_returning_sql(self, sql: str, key_column: str) -> str:
|
|
44
|
+
"""
|
|
45
|
+
获取插入并返回主键的SQL
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
sql: 原始INSERT SQL
|
|
49
|
+
key_column: 主键列名
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
返回主键的INSERT SQL
|
|
53
|
+
"""
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
@abstractmethod
|
|
57
|
+
def get_batch_insert_sql(self, table_name: str, columns: List[str], values: List[Tuple]) -> str:
|
|
58
|
+
"""
|
|
59
|
+
获取批量插入SQL
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
table_name: 表名
|
|
63
|
+
columns: 列名列表
|
|
64
|
+
values: 值列表
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
批量插入SQL
|
|
68
|
+
"""
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
@abstractmethod
|
|
72
|
+
def get_batch_update_sql(self, table_name: str, columns: List[str], values: List[Tuple], pk_column: str) -> str:
|
|
73
|
+
"""
|
|
74
|
+
获取批量更新SQL
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
table_name: 表名
|
|
78
|
+
columns: 列名列表(不含主键)
|
|
79
|
+
values: 值列表(每条记录包含主键和更新值)
|
|
80
|
+
pk_column: 主键列名
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
批量更新SQL
|
|
84
|
+
"""
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
@abstractmethod
|
|
88
|
+
def get_identity_query(self) -> str:
|
|
89
|
+
"""获取获取自增主键的SQL"""
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
@abstractmethod
|
|
93
|
+
def get_now_function(self) -> str:
|
|
94
|
+
"""获取当前时间函数"""
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
@abstractmethod
|
|
98
|
+
def get_concat_function(self, args: List[str]) -> str:
|
|
99
|
+
"""获取字符串拼接函数"""
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
@abstractmethod
|
|
103
|
+
def get_substring_function(self, column: str, start: int, length: Optional[int] = None) -> str:
|
|
104
|
+
"""获取子字符串函数"""
|
|
105
|
+
pass
|
|
106
|
+
|
|
107
|
+
@abstractmethod
|
|
108
|
+
def get_length_function(self, column: str) -> str:
|
|
109
|
+
"""获取长度函数"""
|
|
110
|
+
pass
|
|
111
|
+
|
|
112
|
+
@abstractmethod
|
|
113
|
+
def supports_limit_offset(self) -> bool:
|
|
114
|
+
"""是否支持LIMIT OFFSET语法"""
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
@abstractmethod
|
|
118
|
+
def supports_batch_insert(self) -> bool:
|
|
119
|
+
"""是否支持批量插入"""
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
@abstractmethod
|
|
123
|
+
def get_quote_char(self) -> str:
|
|
124
|
+
"""获取标识符引用字符"""
|
|
125
|
+
pass
|
|
126
|
+
|
|
127
|
+
def quote_identifier(self, identifier: str) -> str:
|
|
128
|
+
"""
|
|
129
|
+
引用标识符(表名、列名等)
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
identifier: 标识符
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
带引号的标识符
|
|
136
|
+
"""
|
|
137
|
+
quote = self.get_quote_char()
|
|
138
|
+
return f"{quote}{identifier}{quote}"
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class MySQLDialect(Dialect):
|
|
142
|
+
"""MySQL数据库方言"""
|
|
143
|
+
|
|
144
|
+
def get_dialect_name(self) -> str:
|
|
145
|
+
return 'mysql'
|
|
146
|
+
|
|
147
|
+
def get_pagination_sql(self, sql: str, offset: int, limit: int) -> str:
|
|
148
|
+
return f"{sql} LIMIT {limit} OFFSET {offset}"
|
|
149
|
+
|
|
150
|
+
def get_insert_returning_sql(self, sql: str, key_column: str) -> str:
|
|
151
|
+
return sql
|
|
152
|
+
|
|
153
|
+
def get_batch_insert_sql(self, table_name: str, columns: List[str], values: List[Tuple]) -> str:
|
|
154
|
+
columns_str = ', '.join(self.quote_identifier(c) for c in columns)
|
|
155
|
+
values_str = ', '.join(
|
|
156
|
+
f"({', '.join('%s' for _ in row)})" for row in values
|
|
157
|
+
)
|
|
158
|
+
return f"INSERT INTO {self.quote_identifier(table_name)} ({columns_str}) VALUES {values_str}"
|
|
159
|
+
|
|
160
|
+
def get_batch_update_sql(self, table_name: str, columns: List[str], values: List[Tuple], pk_column: str) -> str:
|
|
161
|
+
# MySQL批量更新使用CASE WHEN
|
|
162
|
+
set_parts = []
|
|
163
|
+
for col in columns:
|
|
164
|
+
when_parts = []
|
|
165
|
+
for _ in values:
|
|
166
|
+
when_parts.append(f"WHEN {pk_column} = %s THEN %s")
|
|
167
|
+
set_parts.append(f"{self.quote_identifier(col)} = CASE {pk_column} {' '.join(when_parts)} END")
|
|
168
|
+
|
|
169
|
+
set_str = ', '.join(set_parts)
|
|
170
|
+
pk_values = [row[0] for row in values]
|
|
171
|
+
where_str = f"{pk_column} IN ({', '.join('%s' for _ in pk_values)})"
|
|
172
|
+
|
|
173
|
+
return f"UPDATE {self.quote_identifier(table_name)} SET {set_str} WHERE {where_str}"
|
|
174
|
+
|
|
175
|
+
def get_identity_query(self) -> str:
|
|
176
|
+
return "SELECT LAST_INSERT_ID()"
|
|
177
|
+
|
|
178
|
+
def get_now_function(self) -> str:
|
|
179
|
+
return "NOW()"
|
|
180
|
+
|
|
181
|
+
def get_concat_function(self, args: List[str]) -> str:
|
|
182
|
+
return ' CONCAT(' + ', '.join(args) + ')'
|
|
183
|
+
|
|
184
|
+
def get_substring_function(self, column: str, start: int, length: Optional[int] = None) -> str:
|
|
185
|
+
if length:
|
|
186
|
+
return f"SUBSTRING({column}, {start}, {length})"
|
|
187
|
+
return f"SUBSTRING({column}, {start})"
|
|
188
|
+
|
|
189
|
+
def get_length_function(self, column: str) -> str:
|
|
190
|
+
return f"LENGTH({column})"
|
|
191
|
+
|
|
192
|
+
def supports_limit_offset(self) -> bool:
|
|
193
|
+
return True
|
|
194
|
+
|
|
195
|
+
def supports_batch_insert(self) -> bool:
|
|
196
|
+
return True
|
|
197
|
+
|
|
198
|
+
def get_quote_char(self) -> str:
|
|
199
|
+
return '`'
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class PostgreSQLDialect(Dialect):
|
|
203
|
+
"""PostgreSQL数据库方言"""
|
|
204
|
+
|
|
205
|
+
def get_dialect_name(self) -> str:
|
|
206
|
+
return 'postgresql'
|
|
207
|
+
|
|
208
|
+
def get_pagination_sql(self, sql: str, offset: int, limit: int) -> str:
|
|
209
|
+
return f"{sql} LIMIT {limit} OFFSET {offset}"
|
|
210
|
+
|
|
211
|
+
def get_insert_returning_sql(self, sql: str, key_column: str) -> str:
|
|
212
|
+
return f"{sql} RETURNING {self.quote_identifier(key_column)}"
|
|
213
|
+
|
|
214
|
+
def get_batch_insert_sql(self, table_name: str, columns: List[str], values: List[Tuple]) -> str:
|
|
215
|
+
columns_str = ', '.join(self.quote_identifier(c) for c in columns)
|
|
216
|
+
values_str = ', '.join(
|
|
217
|
+
f"({', '.join('%s' for _ in row)})" for row in values
|
|
218
|
+
)
|
|
219
|
+
return f"INSERT INTO {self.quote_identifier(table_name)} ({columns_str}) VALUES {values_str}"
|
|
220
|
+
|
|
221
|
+
def get_batch_update_sql(self, table_name: str, columns: List[str], values: List[Tuple], pk_column: str) -> str:
|
|
222
|
+
# PostgreSQL批量更新使用FROM子句
|
|
223
|
+
set_parts = []
|
|
224
|
+
for col in columns:
|
|
225
|
+
set_parts.append(f"{self.quote_identifier(col)} = updates.{self.quote_identifier(col)}")
|
|
226
|
+
|
|
227
|
+
set_str = ', '.join(set_parts)
|
|
228
|
+
|
|
229
|
+
# 构建VALUES子句作为临时表
|
|
230
|
+
columns_with_pk = [pk_column] + columns
|
|
231
|
+
values_str = ', '.join(
|
|
232
|
+
f"({', '.join('%s' for _ in row)})" for row in values
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
return f"""
|
|
236
|
+
UPDATE {self.quote_identifier(table_name)}
|
|
237
|
+
SET {set_str}
|
|
238
|
+
FROM (VALUES {values_str}) AS updates({', '.join(self.quote_identifier(c) for c in columns_with_pk)})
|
|
239
|
+
WHERE {self.quote_identifier(table_name)}.{self.quote_identifier(pk_column)} = updates.{self.quote_identifier(pk_column)}
|
|
240
|
+
"""
|
|
241
|
+
|
|
242
|
+
def get_identity_query(self) -> str:
|
|
243
|
+
return "SELECT LASTVAL()"
|
|
244
|
+
|
|
245
|
+
def get_now_function(self) -> str:
|
|
246
|
+
return "NOW()"
|
|
247
|
+
|
|
248
|
+
def get_concat_function(self, args: List[str]) -> str:
|
|
249
|
+
return ' || '.join(args)
|
|
250
|
+
|
|
251
|
+
def get_substring_function(self, column: str, start: int, length: Optional[int] = None) -> str:
|
|
252
|
+
if length:
|
|
253
|
+
return f"SUBSTRING({column} FROM {start} FOR {length})"
|
|
254
|
+
return f"SUBSTRING({column} FROM {start})"
|
|
255
|
+
|
|
256
|
+
def get_length_function(self, column: str) -> str:
|
|
257
|
+
return f"LENGTH({column})"
|
|
258
|
+
|
|
259
|
+
def supports_limit_offset(self) -> bool:
|
|
260
|
+
return True
|
|
261
|
+
|
|
262
|
+
def supports_batch_insert(self) -> bool:
|
|
263
|
+
return True
|
|
264
|
+
|
|
265
|
+
def get_quote_char(self) -> str:
|
|
266
|
+
return '"'
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class SQLiteDialect(Dialect):
|
|
270
|
+
"""SQLite数据库方言"""
|
|
271
|
+
|
|
272
|
+
def get_dialect_name(self) -> str:
|
|
273
|
+
return 'sqlite'
|
|
274
|
+
|
|
275
|
+
def get_pagination_sql(self, sql: str, offset: int, limit: int) -> str:
|
|
276
|
+
return f"{sql} LIMIT {limit} OFFSET {offset}"
|
|
277
|
+
|
|
278
|
+
def get_insert_returning_sql(self, sql: str, key_column: str) -> str:
|
|
279
|
+
return sql
|
|
280
|
+
|
|
281
|
+
def get_batch_insert_sql(self, table_name: str, columns: List[str], values: List[Tuple]) -> str:
|
|
282
|
+
columns_str = ', '.join(self.quote_identifier(c) for c in columns)
|
|
283
|
+
values_str = ', '.join(
|
|
284
|
+
f"({', '.join('?' for _ in row)})" for row in values
|
|
285
|
+
)
|
|
286
|
+
return f"INSERT INTO {self.quote_identifier(table_name)} ({columns_str}) VALUES {values_str}"
|
|
287
|
+
|
|
288
|
+
def get_batch_update_sql(self, table_name: str, columns: List[str], values: List[Tuple], pk_column: str) -> str:
|
|
289
|
+
# SQLite批量更新使用CASE WHEN
|
|
290
|
+
set_parts = []
|
|
291
|
+
for col in columns:
|
|
292
|
+
when_parts = []
|
|
293
|
+
for i, row in enumerate(values):
|
|
294
|
+
when_parts.append(f"WHEN {pk_column} = ? THEN ?")
|
|
295
|
+
set_parts.append(f"{self.quote_identifier(col)} = CASE {pk_column} {' '.join(when_parts)} END")
|
|
296
|
+
|
|
297
|
+
set_str = ', '.join(set_parts)
|
|
298
|
+
pk_values = [row[0] for row in values]
|
|
299
|
+
where_str = f"{pk_column} IN ({', '.join('?' for _ in pk_values)})"
|
|
300
|
+
|
|
301
|
+
return f"UPDATE {self.quote_identifier(table_name)} SET {set_str} WHERE {where_str}"
|
|
302
|
+
|
|
303
|
+
def get_identity_query(self) -> str:
|
|
304
|
+
return "SELECT LAST_INSERT_ROWID()"
|
|
305
|
+
|
|
306
|
+
def get_now_function(self) -> str:
|
|
307
|
+
return "datetime('now')"
|
|
308
|
+
|
|
309
|
+
def get_concat_function(self, args: List[str]) -> str:
|
|
310
|
+
return ' || '.join(args)
|
|
311
|
+
|
|
312
|
+
def get_substring_function(self, column: str, start: int, length: Optional[int] = None) -> str:
|
|
313
|
+
if length:
|
|
314
|
+
return f"SUBSTR({column}, {start}, {length})"
|
|
315
|
+
return f"SUBSTR({column}, {start})"
|
|
316
|
+
|
|
317
|
+
def get_length_function(self, column: str) -> str:
|
|
318
|
+
return f"LENGTH({column})"
|
|
319
|
+
|
|
320
|
+
def supports_limit_offset(self) -> bool:
|
|
321
|
+
return True
|
|
322
|
+
|
|
323
|
+
def supports_batch_insert(self) -> bool:
|
|
324
|
+
return True
|
|
325
|
+
|
|
326
|
+
def get_quote_char(self) -> str:
|
|
327
|
+
return '"'
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class OracleDialect(Dialect):
|
|
331
|
+
"""Oracle数据库方言"""
|
|
332
|
+
|
|
333
|
+
def get_dialect_name(self) -> str:
|
|
334
|
+
return 'oracle'
|
|
335
|
+
|
|
336
|
+
def get_pagination_sql(self, sql: str, offset: int, limit: int) -> str:
|
|
337
|
+
# Oracle使用ROWNUM进行分页
|
|
338
|
+
offset_val = offset + 1
|
|
339
|
+
return f"""
|
|
340
|
+
SELECT * FROM (
|
|
341
|
+
SELECT t.*, ROWNUM rn FROM (
|
|
342
|
+
{sql}
|
|
343
|
+
) t WHERE ROWNUM <= {offset + limit}
|
|
344
|
+
) WHERE rn >= {offset_val}
|
|
345
|
+
"""
|
|
346
|
+
|
|
347
|
+
def get_insert_returning_sql(self, sql: str, key_column: str) -> str:
|
|
348
|
+
return f"{sql} RETURNING {self.quote_identifier(key_column)} INTO :id"
|
|
349
|
+
|
|
350
|
+
def get_batch_insert_sql(self, table_name: str, columns: List[str], values: List[Tuple]) -> str:
|
|
351
|
+
# Oracle批量插入使用UNION ALL
|
|
352
|
+
columns_str = ', '.join(self.quote_identifier(c) for c in columns)
|
|
353
|
+
values_parts = []
|
|
354
|
+
for i, row in enumerate(values):
|
|
355
|
+
row_values = []
|
|
356
|
+
for j, _ in enumerate(row):
|
|
357
|
+
row_values.append(f":{i * len(row) + j + 1}")
|
|
358
|
+
values_parts.append(f"SELECT {', '.join(row_values)} FROM DUAL")
|
|
359
|
+
|
|
360
|
+
values_str = ' UNION ALL '.join(values_parts)
|
|
361
|
+
return f"INSERT INTO {self.quote_identifier(table_name)} ({columns_str}) {values_str}"
|
|
362
|
+
|
|
363
|
+
def get_batch_update_sql(self, table_name: str, columns: List[str], values: List[Tuple], pk_column: str) -> str:
|
|
364
|
+
# Oracle批量更新使用MERGE语句
|
|
365
|
+
columns_with_pk = [pk_column] + columns
|
|
366
|
+
|
|
367
|
+
# 构建VALUES子句
|
|
368
|
+
values_parts = []
|
|
369
|
+
param_index = 1
|
|
370
|
+
for row in values:
|
|
371
|
+
row_values = []
|
|
372
|
+
for _ in row:
|
|
373
|
+
row_values.append(f":{param_index}")
|
|
374
|
+
param_index += 1
|
|
375
|
+
values_parts.append(f"({', '.join(row_values)})")
|
|
376
|
+
|
|
377
|
+
values_str = ' UNION ALL '.join(
|
|
378
|
+
f"SELECT {', '.join(values_parts[i])} FROM DUAL" for i in range(len(values_parts))
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
# 构建SET子句
|
|
382
|
+
set_parts = []
|
|
383
|
+
for col in columns:
|
|
384
|
+
set_parts.append(f"{self.quote_identifier(col)} = source.{self.quote_identifier(col)}")
|
|
385
|
+
set_str = ', '.join(set_parts)
|
|
386
|
+
|
|
387
|
+
return f"""
|
|
388
|
+
MERGE INTO {self.quote_identifier(table_name)} target
|
|
389
|
+
USING (SELECT {', '.join(self.quote_identifier(c) for c in columns_with_pk)} FROM ({values_str})) source
|
|
390
|
+
ON (target.{self.quote_identifier(pk_column)} = source.{self.quote_identifier(pk_column)})
|
|
391
|
+
WHEN MATCHED THEN UPDATE SET {set_str}
|
|
392
|
+
"""
|
|
393
|
+
|
|
394
|
+
def get_identity_query(self) -> str:
|
|
395
|
+
return "SELECT SEQ_CURRVAL FROM DUAL"
|
|
396
|
+
|
|
397
|
+
def get_now_function(self) -> str:
|
|
398
|
+
return "SYSDATE"
|
|
399
|
+
|
|
400
|
+
def get_concat_function(self, args: List[str]) -> str:
|
|
401
|
+
return ' || '.join(args)
|
|
402
|
+
|
|
403
|
+
def get_substring_function(self, column: str, start: int, length: Optional[int] = None) -> str:
|
|
404
|
+
if length:
|
|
405
|
+
return f"SUBSTR({column}, {start}, {length})"
|
|
406
|
+
return f"SUBSTR({column}, {start})"
|
|
407
|
+
|
|
408
|
+
def get_length_function(self, column: str) -> str:
|
|
409
|
+
return f"LENGTH({column})"
|
|
410
|
+
|
|
411
|
+
def supports_limit_offset(self) -> bool:
|
|
412
|
+
return False
|
|
413
|
+
|
|
414
|
+
def supports_batch_insert(self) -> bool:
|
|
415
|
+
return True
|
|
416
|
+
|
|
417
|
+
def get_quote_char(self) -> str:
|
|
418
|
+
return '"'
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def get_dialect(dialect_name: str) -> Dialect:
|
|
422
|
+
"""
|
|
423
|
+
根据方言名称获取方言实例
|
|
424
|
+
|
|
425
|
+
Args:
|
|
426
|
+
dialect_name: 方言名称(mysql/postgresql/sqlite/oracle)
|
|
427
|
+
|
|
428
|
+
Returns:
|
|
429
|
+
方言实例
|
|
430
|
+
|
|
431
|
+
Raises:
|
|
432
|
+
ValueError: 不支持的方言
|
|
433
|
+
"""
|
|
434
|
+
dialect_map = {
|
|
435
|
+
'mysql': MySQLDialect(),
|
|
436
|
+
'postgresql': PostgreSQLDialect(),
|
|
437
|
+
'sqlite': SQLiteDialect(),
|
|
438
|
+
'oracle': OracleDialect()
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
dialect = dialect_map.get(dialect_name.lower())
|
|
442
|
+
if not dialect:
|
|
443
|
+
raise ValueError(f"不支持的数据库方言: {dialect_name}")
|
|
444
|
+
|
|
445
|
+
return dialect
|