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.
Files changed (175) hide show
  1. spring/__init__.py +66 -0
  2. spring/ai/__init__.py +78 -0
  3. spring/ai/advisors.py +139 -0
  4. spring/ai/annotations.py +74 -0
  5. spring/ai/autoconfig.py +481 -0
  6. spring/ai/core.py +391 -0
  7. spring/ai/etl.py +188 -0
  8. spring/ai/memory.py +109 -0
  9. spring/ai/observability.py +129 -0
  10. spring/ai/providers.py +789 -0
  11. spring/ai/resilience.py +258 -0
  12. spring/ai/tools.py +106 -0
  13. spring/ai/vectorstore.py +303 -0
  14. spring/annotations/__init__.py +188 -0
  15. spring/annotations/cache.py +126 -0
  16. spring/annotations/cloud.py +207 -0
  17. spring/annotations/conditional.py +272 -0
  18. spring/annotations/core.py +864 -0
  19. spring/annotations/messaging.py +107 -0
  20. spring/aop/__init__.py +4 -0
  21. spring/aop/cloud_aop.py +404 -0
  22. spring/aop/comprehensive_aop.py +1015 -0
  23. spring/aop/method_interceptor.py +19 -0
  24. spring/aop/proxy_factory.py +55 -0
  25. spring/cloud/__init__.py +76 -0
  26. spring/cloud/discovery.py +364 -0
  27. spring/cloud/feign.py +469 -0
  28. spring/cloud/gateway.py +452 -0
  29. spring/cloud/load_balancer.py +149 -0
  30. spring/cloud/seata.py +557 -0
  31. spring/cloud/sentinel.py +525 -0
  32. spring/cloud/tracer.py +337 -0
  33. spring/config/__init__.py +21 -0
  34. spring/config/binding.py +206 -0
  35. spring/config/config_loader.py +405 -0
  36. spring/context/__init__.py +13 -0
  37. spring/context/application_context.py +589 -0
  38. spring/context/bean_definition.py +70 -0
  39. spring/context/bean_factory.py +1052 -0
  40. spring/context/registry.py +58 -0
  41. spring/context/scanner.py +106 -0
  42. spring/core/__init__.py +3 -0
  43. spring/core/graceful_shutdown.py +196 -0
  44. spring/core/typing_utils.py +50 -0
  45. spring/csv/__init__.py +52 -0
  46. spring/csv/annotations.py +402 -0
  47. spring/csv/converters.py +69 -0
  48. spring/csv/easy_csv.py +95 -0
  49. spring/csv/exceptions.py +27 -0
  50. spring/csv/reader.py +195 -0
  51. spring/csv/writer.py +155 -0
  52. spring/data/__init__.py +54 -0
  53. spring/data/page.py +181 -0
  54. spring/data/repository.py +274 -0
  55. spring/data/specification.py +228 -0
  56. spring/datasource/__init__.py +66 -0
  57. spring/datasource/annotations.py +133 -0
  58. spring/datasource/context.py +69 -0
  59. spring/datasource/dynamic.py +148 -0
  60. spring/event/__init__.py +7 -0
  61. spring/event/publisher.py +69 -0
  62. spring/excel/__init__.py +51 -0
  63. spring/excel/annotations.py +405 -0
  64. spring/excel/converters.py +231 -0
  65. spring/excel/easy_excel.py +94 -0
  66. spring/excel/exceptions.py +31 -0
  67. spring/excel/reader.py +254 -0
  68. spring/excel/style.py +95 -0
  69. spring/excel/writer.py +197 -0
  70. spring/i18n/__init__.py +97 -0
  71. spring/i18n/accessor.py +94 -0
  72. spring/i18n/auto_config.py +177 -0
  73. spring/i18n/holder.py +106 -0
  74. spring/i18n/locale.py +152 -0
  75. spring/i18n/locale_resolver.py +367 -0
  76. spring/i18n/message_source.py +250 -0
  77. spring/i18n/middleware.py +79 -0
  78. spring/i18n/properties.py +168 -0
  79. spring/i18n/sources.py +255 -0
  80. spring/logging/__init__.py +1 -0
  81. spring/logging/loguru_logger.py +228 -0
  82. spring/main.py +378 -0
  83. spring/messaging/__init__.py +1 -0
  84. spring/messaging/rabbitmq.py +302 -0
  85. spring/monitoring/__init__.py +1 -0
  86. spring/monitoring/prometheus.py +199 -0
  87. spring/orm/__init__.py +258 -0
  88. spring/orm/database.py +222 -0
  89. spring/orm/ddl_auto.py +1217 -0
  90. spring/orm/migration.py +419 -0
  91. spring/orm/mybatis_integration.py +400 -0
  92. spring/orm/pymybatis/__init__.py +86 -0
  93. spring/orm/pymybatis/annotations/__init__.py +30 -0
  94. spring/orm/pymybatis/annotations/annotations.py +332 -0
  95. spring/orm/pymybatis/cache/__init__.py +47 -0
  96. spring/orm/pymybatis/cache/cache.py +371 -0
  97. spring/orm/pymybatis/cache/redis_cache.py +434 -0
  98. spring/orm/pymybatis/circuit_breaker/__init__.py +21 -0
  99. spring/orm/pymybatis/circuit_breaker/circuit_breaker.py +424 -0
  100. spring/orm/pymybatis/configuration.py +525 -0
  101. spring/orm/pymybatis/core/__init__.py +10 -0
  102. spring/orm/pymybatis/core/sql_session.py +1382 -0
  103. spring/orm/pymybatis/core/sql_session_factory.py +76 -0
  104. spring/orm/pymybatis/dialect/__init__.py +9 -0
  105. spring/orm/pymybatis/dialect/dialect.py +445 -0
  106. spring/orm/pymybatis/dynamic_sql/__init__.py +9 -0
  107. spring/orm/pymybatis/dynamic_sql/dynamic_sql.py +900 -0
  108. spring/orm/pymybatis/interceptor/__init__.py +31 -0
  109. spring/orm/pymybatis/interceptor/interceptor.py +427 -0
  110. spring/orm/pymybatis/mapper/__init__.py +9 -0
  111. spring/orm/pymybatis/mapper/mapper.py +540 -0
  112. spring/orm/pymybatis/metrics/__init__.py +41 -0
  113. spring/orm/pymybatis/metrics/metrics.py +595 -0
  114. spring/orm/pymybatis/pool/__init__.py +9 -0
  115. spring/orm/pymybatis/pool/connection_pool.py +711 -0
  116. spring/orm/pymybatis/security/__init__.py +19 -0
  117. spring/orm/pymybatis/security/access_control.py +415 -0
  118. spring/orm/pymybatis/security/password_encoder.py +293 -0
  119. spring/orm/pymybatis/security/sensitive_data_masker.py +326 -0
  120. spring/orm/pymybatis/security/sql_injection_detector.py +675 -0
  121. spring/orm/pymybatis/transaction/__init__.py +9 -0
  122. spring/orm/pymybatis/transaction/transaction.py +288 -0
  123. spring/orm/pymybatis/type_handler/__init__.py +37 -0
  124. spring/orm/pymybatis/type_handler/type_handler.py +473 -0
  125. spring/orm/pymybatis/version.py +9 -0
  126. spring/orm/pymybatis/xml_parser/__init__.py +9 -0
  127. spring/orm/pymybatis/xml_parser/xml_parser.py +761 -0
  128. spring/retry/__init__.py +12 -0
  129. spring/retry/retry_annotations.py +71 -0
  130. spring/retry/retry_decorator.py +155 -0
  131. spring/scheduling/__init__.py +3 -0
  132. spring/scheduling/scheduler.py +389 -0
  133. spring/security/__init__.py +39 -0
  134. spring/security/jwt_utils.py +281 -0
  135. spring/security/replay_protection.py +206 -0
  136. spring/security/secret_manager.py +226 -0
  137. spring/security/security_aop.py +248 -0
  138. spring/security/security_context.py +172 -0
  139. spring/test/__init__.py +45 -0
  140. spring/test/slicing.py +341 -0
  141. spring/tracing/__init__.py +11 -0
  142. spring/tracing/skywalking.py +229 -0
  143. spring/tx/__init__.py +52 -0
  144. spring/tx/events.py +172 -0
  145. spring/tx/synchronization.py +143 -0
  146. spring/utils/__init__.py +5 -0
  147. spring/utils/banner.py +32 -0
  148. spring/utils/logger.py +73 -0
  149. spring/utils/redis_client.py +526 -0
  150. spring/validation/__init__.py +55 -0
  151. spring/validation/aop.py +141 -0
  152. spring/validation/constraints.py +357 -0
  153. spring/validation/exceptions.py +55 -0
  154. spring/validation/validator.py +139 -0
  155. spring/web/__init__.py +12 -0
  156. spring/web/actuator.py +319 -0
  157. spring/web/exception_handler.py +61 -0
  158. spring/web/health.py +399 -0
  159. spring/web/interceptor.py +91 -0
  160. spring/web/result.py +44 -0
  161. spring/web/swagger.py +601 -0
  162. spring/web/web_context.py +755 -0
  163. spring/websocket/__init__.py +86 -0
  164. spring/websocket/annotations.py +169 -0
  165. spring/websocket/broker.py +238 -0
  166. spring/websocket/exceptions.py +26 -0
  167. spring/websocket/handler.py +243 -0
  168. spring/websocket/router.py +526 -0
  169. spring/websocket/session.py +216 -0
  170. springbootai-1.8.0.dist-info/METADATA +2796 -0
  171. springbootai-1.8.0.dist-info/RECORD +175 -0
  172. springbootai-1.8.0.dist-info/WHEEL +5 -0
  173. springbootai-1.8.0.dist-info/entry_points.txt +2 -0
  174. springbootai-1.8.0.dist-info/licenses/LICENSE +7 -0
  175. 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
@@ -0,0 +1,9 @@
1
+ """
2
+ PyMyBatis动态SQL模块
3
+
4
+ 处理if、where、foreach等动态SQL标签
5
+ """
6
+
7
+ from .dynamic_sql import DynamicSQLProcessor, SecurityError
8
+
9
+ __all__ = ['DynamicSQLProcessor', 'SecurityError']