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,540 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyMyBatis映射器模块
|
|
3
|
+
|
|
4
|
+
实现Mapper接口的动态代理,支持XML和注解两种SQL定义方式
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import importlib
|
|
8
|
+
import inspect
|
|
9
|
+
import types
|
|
10
|
+
from dataclasses import asdict, is_dataclass
|
|
11
|
+
from collections.abc import Mapping
|
|
12
|
+
from typing import Any, Dict, Type, Optional, Annotated, Union, get_args, get_origin, get_type_hints
|
|
13
|
+
|
|
14
|
+
from ..annotations import Param
|
|
15
|
+
|
|
16
|
+
_UNION_ORIGINS = {Union}
|
|
17
|
+
if getattr(types, 'UnionType', None) is not None:
|
|
18
|
+
_UNION_ORIGINS.add(types.UnionType)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Mapper:
|
|
22
|
+
"""
|
|
23
|
+
Mapper基类
|
|
24
|
+
|
|
25
|
+
所有Mapper接口都应继承此类
|
|
26
|
+
"""
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class MapperProxy:
|
|
31
|
+
"""
|
|
32
|
+
Mapper代理类
|
|
33
|
+
|
|
34
|
+
实现动态代理,将Mapper接口方法调用转换为SQL执行
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, mapper_class: Type, sql_session: Any):
|
|
38
|
+
"""
|
|
39
|
+
初始化Mapper代理
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
mapper_class: Mapper类
|
|
43
|
+
sql_session: SqlSession实例
|
|
44
|
+
"""
|
|
45
|
+
self.mapper_class = mapper_class
|
|
46
|
+
self.sql_session = sql_session
|
|
47
|
+
self._register_result_maps()
|
|
48
|
+
|
|
49
|
+
def _register_result_maps(self) -> None:
|
|
50
|
+
namespace = f"{self.mapper_class.__module__}.{self.mapper_class.__name__}"
|
|
51
|
+
for result_map in getattr(self.mapper_class, '__result_maps__', []):
|
|
52
|
+
self.sql_session.result_maps[result_map.id] = result_map
|
|
53
|
+
self.sql_session.result_maps[f"{namespace}.{result_map.id}"] = result_map
|
|
54
|
+
|
|
55
|
+
def __getattr__(self, name: str):
|
|
56
|
+
"""
|
|
57
|
+
获取属性
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
name: 属性名
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
方法执行结果
|
|
64
|
+
"""
|
|
65
|
+
method = getattr(self.mapper_class, name, None)
|
|
66
|
+
|
|
67
|
+
if method is None or not callable(method):
|
|
68
|
+
raise AttributeError(f"Mapper {self.mapper_class.__name__} 没有方法: {name}")
|
|
69
|
+
|
|
70
|
+
def wrapper(*args, **kwargs):
|
|
71
|
+
return self._execute_method(method, *args, **kwargs)
|
|
72
|
+
|
|
73
|
+
return wrapper
|
|
74
|
+
|
|
75
|
+
def _execute_method(self, method, *args, **kwargs):
|
|
76
|
+
"""
|
|
77
|
+
执行方法
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
method: 方法对象
|
|
81
|
+
args: 位置参数
|
|
82
|
+
kwargs: 关键字参数
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
执行结果
|
|
86
|
+
"""
|
|
87
|
+
# 获取被装饰的原始方法
|
|
88
|
+
wrapped_method = getattr(method, '__func__', method)
|
|
89
|
+
|
|
90
|
+
# 获取方法注解(通过装饰器附加的属性)
|
|
91
|
+
select_annotation = getattr(wrapped_method, 'select', None)
|
|
92
|
+
insert_annotation = getattr(wrapped_method, 'insert', None)
|
|
93
|
+
update_annotation = getattr(wrapped_method, 'update', None)
|
|
94
|
+
delete_annotation = getattr(wrapped_method, 'delete', None)
|
|
95
|
+
select_provider = getattr(wrapped_method, 'select_provider', None)
|
|
96
|
+
insert_provider = getattr(wrapped_method, 'insert_provider', None)
|
|
97
|
+
update_provider = getattr(wrapped_method, 'update_provider', None)
|
|
98
|
+
delete_provider = getattr(wrapped_method, 'delete_provider', None)
|
|
99
|
+
options = getattr(wrapped_method, 'options', None)
|
|
100
|
+
transaction = getattr(
|
|
101
|
+
wrapped_method,
|
|
102
|
+
'transactional',
|
|
103
|
+
getattr(self.mapper_class, 'transactional', None),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# 解析参数
|
|
107
|
+
params = self._parse_params(method, args, kwargs)
|
|
108
|
+
|
|
109
|
+
def execute():
|
|
110
|
+
if options and options.flush_cache:
|
|
111
|
+
self.sql_session.sql_cache.clear()
|
|
112
|
+
|
|
113
|
+
if select_annotation or select_provider:
|
|
114
|
+
provider_options = select_provider.options if select_provider else {}
|
|
115
|
+
select_sql = (
|
|
116
|
+
self._call_sql_provider(select_provider, params)
|
|
117
|
+
if select_provider else select_annotation.value
|
|
118
|
+
)
|
|
119
|
+
result_map = self._resolve_result_map(
|
|
120
|
+
provider_options.get('result_map') if select_provider
|
|
121
|
+
else select_annotation.result_map
|
|
122
|
+
)
|
|
123
|
+
fetch_size = (
|
|
124
|
+
options.fetch_size if options and options.fetch_size is not None
|
|
125
|
+
else provider_options.get('fetch_size') if select_provider
|
|
126
|
+
else select_annotation.fetch_size
|
|
127
|
+
)
|
|
128
|
+
timeout = (
|
|
129
|
+
options.timeout if options and options.timeout is not None
|
|
130
|
+
else provider_options.get('timeout') if select_provider
|
|
131
|
+
else select_annotation.timeout
|
|
132
|
+
)
|
|
133
|
+
use_cache = (
|
|
134
|
+
options.use_cache if options else provider_options.get('cache', True)
|
|
135
|
+
if select_provider else select_annotation.cache
|
|
136
|
+
)
|
|
137
|
+
result_type = (
|
|
138
|
+
(provider_options.get('result_type') or
|
|
139
|
+
self._result_type_from_return_annotation(method))
|
|
140
|
+
if select_provider else
|
|
141
|
+
(select_annotation.result_type or
|
|
142
|
+
self._result_type_from_return_annotation(method))
|
|
143
|
+
)
|
|
144
|
+
single = self._returns_single(method)
|
|
145
|
+
if single:
|
|
146
|
+
result = self.sql_session.select_one(
|
|
147
|
+
select_sql,
|
|
148
|
+
params,
|
|
149
|
+
result_map=result_map,
|
|
150
|
+
use_cache=use_cache,
|
|
151
|
+
fetch_size=fetch_size,
|
|
152
|
+
timeout=timeout,
|
|
153
|
+
)
|
|
154
|
+
else:
|
|
155
|
+
result = self.sql_session.select(
|
|
156
|
+
select_sql,
|
|
157
|
+
params,
|
|
158
|
+
result_map=result_map,
|
|
159
|
+
use_cache=use_cache,
|
|
160
|
+
fetch_size=fetch_size,
|
|
161
|
+
timeout=timeout,
|
|
162
|
+
)
|
|
163
|
+
return self._apply_result_type(result, result_type)
|
|
164
|
+
|
|
165
|
+
timeout = options.timeout if options else None
|
|
166
|
+
if insert_annotation or insert_provider:
|
|
167
|
+
provider_options = insert_provider.options if insert_provider else {}
|
|
168
|
+
insert_sql = (
|
|
169
|
+
self._call_sql_provider(insert_provider, params)
|
|
170
|
+
if insert_provider else insert_annotation.value
|
|
171
|
+
)
|
|
172
|
+
use_generated_keys = (
|
|
173
|
+
insert_annotation.use_generated_keys if insert_annotation else False
|
|
174
|
+
) or bool(options and options.use_generated_keys) or bool(
|
|
175
|
+
provider_options.get('use_generated_keys', False)
|
|
176
|
+
)
|
|
177
|
+
key_property = (
|
|
178
|
+
insert_annotation.key_property if insert_annotation else None
|
|
179
|
+
) or (options.key_property if options else None) or provider_options.get('key_property')
|
|
180
|
+
result = self.sql_session.insert(
|
|
181
|
+
insert_sql,
|
|
182
|
+
params,
|
|
183
|
+
use_generated_keys=use_generated_keys,
|
|
184
|
+
timeout=timeout,
|
|
185
|
+
)
|
|
186
|
+
if use_generated_keys and key_property:
|
|
187
|
+
self._assign_generated_key(
|
|
188
|
+
args, kwargs, key_property, result
|
|
189
|
+
)
|
|
190
|
+
return result
|
|
191
|
+
if update_annotation or update_provider:
|
|
192
|
+
provider_options = update_provider.options if update_provider else {}
|
|
193
|
+
update_sql = (
|
|
194
|
+
self._call_sql_provider(update_provider, params)
|
|
195
|
+
if update_provider else update_annotation.value
|
|
196
|
+
)
|
|
197
|
+
return self.sql_session.update(
|
|
198
|
+
update_sql,
|
|
199
|
+
params,
|
|
200
|
+
timeout=timeout or (
|
|
201
|
+
provider_options.get('timeout') if update_provider
|
|
202
|
+
else update_annotation.timeout
|
|
203
|
+
),
|
|
204
|
+
)
|
|
205
|
+
if delete_annotation or delete_provider:
|
|
206
|
+
provider_options = delete_provider.options if delete_provider else {}
|
|
207
|
+
delete_sql = (
|
|
208
|
+
self._call_sql_provider(delete_provider, params)
|
|
209
|
+
if delete_provider else delete_annotation.value
|
|
210
|
+
)
|
|
211
|
+
return self.sql_session.delete(
|
|
212
|
+
delete_sql,
|
|
213
|
+
params,
|
|
214
|
+
timeout=timeout or (
|
|
215
|
+
provider_options.get('timeout') if delete_provider
|
|
216
|
+
else delete_annotation.timeout
|
|
217
|
+
),
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
namespace = self.mapper_class.__module__ + '.' + self.mapper_class.__name__
|
|
221
|
+
statement_id = f"{namespace}.{method.__name__}"
|
|
222
|
+
statement = self.sql_session.get_mapped_statement(statement_id)
|
|
223
|
+
if statement is not None and statement.sql_type == 'SELECT':
|
|
224
|
+
if self._returns_single(method):
|
|
225
|
+
result = self.sql_session.select_one(statement_id, params)
|
|
226
|
+
else:
|
|
227
|
+
result = self.sql_session.select(statement_id, params)
|
|
228
|
+
return self._apply_result_type(
|
|
229
|
+
result, self._result_type_from_return_annotation(method)
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
result = self.sql_session.execute(statement_id, params)
|
|
233
|
+
if (
|
|
234
|
+
statement is not None
|
|
235
|
+
and statement.sql_type == 'INSERT'
|
|
236
|
+
and statement.use_generated_keys
|
|
237
|
+
and statement.key_property
|
|
238
|
+
):
|
|
239
|
+
self._assign_generated_key(args, kwargs, statement.key_property, result)
|
|
240
|
+
return result
|
|
241
|
+
|
|
242
|
+
if transaction is None:
|
|
243
|
+
return execute()
|
|
244
|
+
normalized_propagation = str(transaction.propagation).upper()
|
|
245
|
+
|
|
246
|
+
deferred = None
|
|
247
|
+
deferred_traceback = None
|
|
248
|
+
with self.sql_session.transaction(
|
|
249
|
+
isolation_level=transaction.isolation,
|
|
250
|
+
propagation=normalized_propagation,
|
|
251
|
+
):
|
|
252
|
+
try:
|
|
253
|
+
return execute()
|
|
254
|
+
except Exception as exc:
|
|
255
|
+
if not transaction.rollback_for or any(
|
|
256
|
+
isinstance(exc, exc_type) for exc_type in transaction.rollback_for
|
|
257
|
+
):
|
|
258
|
+
raise
|
|
259
|
+
deferred = exc
|
|
260
|
+
deferred_traceback = exc.__traceback__
|
|
261
|
+
raise deferred.with_traceback(deferred_traceback)
|
|
262
|
+
|
|
263
|
+
@staticmethod
|
|
264
|
+
def _call_sql_provider(provider: Any, params: Dict[str, Any]) -> str:
|
|
265
|
+
"""Resolve and invoke a provider, accepting Java- and Python-style APIs."""
|
|
266
|
+
if provider is None or provider.provider_type is None:
|
|
267
|
+
raise ValueError("Provider 注解必须提供 provider_type/type/value")
|
|
268
|
+
target = provider.provider_type
|
|
269
|
+
if isinstance(target, str):
|
|
270
|
+
module_name, _, member_name = target.rpartition('.')
|
|
271
|
+
if not module_name:
|
|
272
|
+
raise ImportError(f"Provider 类型必须是可导入的全限定名称: {target}")
|
|
273
|
+
target = getattr(importlib.import_module(module_name), member_name)
|
|
274
|
+
|
|
275
|
+
method_name = provider.method
|
|
276
|
+
if method_name:
|
|
277
|
+
method = getattr(target, method_name, None)
|
|
278
|
+
if method is None and inspect.isclass(target):
|
|
279
|
+
method = getattr(target(), method_name, None)
|
|
280
|
+
elif callable(target):
|
|
281
|
+
method = target
|
|
282
|
+
else:
|
|
283
|
+
method = getattr(target, 'provide_sql', None) or getattr(target, 'sql', None)
|
|
284
|
+
if not callable(method):
|
|
285
|
+
raise TypeError("Provider 必须是可调用对象,或包含指定方法/provide_sql 方法")
|
|
286
|
+
|
|
287
|
+
try:
|
|
288
|
+
signature = inspect.signature(method)
|
|
289
|
+
positional = [p for p in signature.parameters.values()
|
|
290
|
+
if p.kind in (p.POSITIONAL_ONLY, p.POSITIONAL_OR_KEYWORD)]
|
|
291
|
+
accepts_kwargs = any(p.kind == p.VAR_KEYWORD for p in signature.parameters.values())
|
|
292
|
+
if accepts_kwargs or (not positional and signature.parameters):
|
|
293
|
+
result = method(**params)
|
|
294
|
+
elif positional:
|
|
295
|
+
result = method(params)
|
|
296
|
+
else:
|
|
297
|
+
result = method()
|
|
298
|
+
except (TypeError, ValueError):
|
|
299
|
+
# C-extension callables may not expose signatures; the dict form
|
|
300
|
+
# is the least surprising provider contract.
|
|
301
|
+
result = method(params)
|
|
302
|
+
if not isinstance(result, str) or not result.strip():
|
|
303
|
+
raise ValueError("SQL Provider 必须返回非空 SQL 字符串")
|
|
304
|
+
return result
|
|
305
|
+
|
|
306
|
+
def _resolve_result_map(self, result_map: Any) -> Optional[str]:
|
|
307
|
+
if result_map is None:
|
|
308
|
+
return None
|
|
309
|
+
if not isinstance(result_map, str):
|
|
310
|
+
self.sql_session.result_maps[result_map.id] = result_map
|
|
311
|
+
return result_map.id
|
|
312
|
+
if result_map in self.sql_session.result_maps:
|
|
313
|
+
return result_map
|
|
314
|
+
namespaced = f"{self.mapper_class.__module__}.{self.mapper_class.__name__}.{result_map}"
|
|
315
|
+
return namespaced if namespaced in self.sql_session.result_maps else result_map
|
|
316
|
+
|
|
317
|
+
def _apply_result_type(self, result: Any, result_type: Any) -> Any:
|
|
318
|
+
target_type = self._resolve_result_type(result_type)
|
|
319
|
+
if target_type is None or result is None:
|
|
320
|
+
return result
|
|
321
|
+
if isinstance(result, list):
|
|
322
|
+
return [self._construct_result(target_type, item) for item in result]
|
|
323
|
+
return self._construct_result(target_type, result)
|
|
324
|
+
|
|
325
|
+
def _resolve_result_type(self, result_type: Any) -> Optional[Type]:
|
|
326
|
+
if result_type is None or result_type in ('dict', 'builtins.dict'):
|
|
327
|
+
return None
|
|
328
|
+
if isinstance(result_type, type):
|
|
329
|
+
return result_type
|
|
330
|
+
if not isinstance(result_type, str):
|
|
331
|
+
raise TypeError("result_type 必须是类型或可导入的类型名称")
|
|
332
|
+
if '.' in result_type:
|
|
333
|
+
module_name, type_name = result_type.rsplit('.', 1)
|
|
334
|
+
return getattr(importlib.import_module(module_name), type_name)
|
|
335
|
+
module = importlib.import_module(self.mapper_class.__module__)
|
|
336
|
+
return getattr(module, result_type)
|
|
337
|
+
|
|
338
|
+
@staticmethod
|
|
339
|
+
def _return_annotation(method) -> Any:
|
|
340
|
+
try:
|
|
341
|
+
return get_type_hints(method, include_extras=True).get('return')
|
|
342
|
+
except (NameError, TypeError):
|
|
343
|
+
return inspect.signature(method).return_annotation
|
|
344
|
+
|
|
345
|
+
def _returns_single(self, method) -> bool:
|
|
346
|
+
annotation = self._return_annotation(method)
|
|
347
|
+
if annotation is not inspect.Signature.empty:
|
|
348
|
+
origin = get_origin(annotation)
|
|
349
|
+
if origin in {list, tuple, set, dict}:
|
|
350
|
+
return False
|
|
351
|
+
if origin in _UNION_ORIGINS:
|
|
352
|
+
candidates = [value for value in get_args(annotation) if value is not type(None)]
|
|
353
|
+
return len(candidates) == 1 and not self._is_collection_type(candidates[0])
|
|
354
|
+
return not self._is_collection_type(annotation)
|
|
355
|
+
|
|
356
|
+
method_name = method.__name__
|
|
357
|
+
return (
|
|
358
|
+
method_name.startswith('find_by_')
|
|
359
|
+
or method_name.startswith('get_by_')
|
|
360
|
+
or method_name == 'find_one'
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
@staticmethod
|
|
364
|
+
def _is_collection_type(annotation: Any) -> bool:
|
|
365
|
+
return get_origin(annotation) in {list, tuple, set, dict} or annotation in {
|
|
366
|
+
list, tuple, set, dict
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
def _result_type_from_return_annotation(self, method) -> Optional[Type]:
|
|
370
|
+
annotation = self._return_annotation(method)
|
|
371
|
+
if annotation is inspect.Signature.empty:
|
|
372
|
+
return None
|
|
373
|
+
origin = get_origin(annotation)
|
|
374
|
+
if origin in {list, tuple, set}:
|
|
375
|
+
args = get_args(annotation)
|
|
376
|
+
return args[0] if args else None
|
|
377
|
+
if origin is dict:
|
|
378
|
+
return None
|
|
379
|
+
if origin in _UNION_ORIGINS:
|
|
380
|
+
candidates = [value for value in get_args(annotation) if value is not type(None)]
|
|
381
|
+
return candidates[0] if len(candidates) == 1 else None
|
|
382
|
+
return annotation if isinstance(annotation, type) else None
|
|
383
|
+
|
|
384
|
+
@staticmethod
|
|
385
|
+
def _construct_result(target_type: Type, value: Any) -> Any:
|
|
386
|
+
if isinstance(value, target_type):
|
|
387
|
+
return value
|
|
388
|
+
if not isinstance(value, Mapping):
|
|
389
|
+
return target_type(value)
|
|
390
|
+
try:
|
|
391
|
+
return target_type(**value)
|
|
392
|
+
except TypeError:
|
|
393
|
+
instance = target_type()
|
|
394
|
+
for key, item in value.items():
|
|
395
|
+
setattr(instance, key, item)
|
|
396
|
+
return instance
|
|
397
|
+
|
|
398
|
+
@staticmethod
|
|
399
|
+
def _assign_generated_key(args, kwargs, property_name: str, value: Any) -> None:
|
|
400
|
+
for candidate in list(args) + list(kwargs.values()):
|
|
401
|
+
if isinstance(candidate, dict):
|
|
402
|
+
candidate[property_name] = value
|
|
403
|
+
return
|
|
404
|
+
if hasattr(candidate, '__dict__'):
|
|
405
|
+
setattr(candidate, property_name, value)
|
|
406
|
+
return
|
|
407
|
+
|
|
408
|
+
def _parse_params(self, method, args, kwargs) -> Dict[str, Any]:
|
|
409
|
+
"""
|
|
410
|
+
解析方法参数
|
|
411
|
+
|
|
412
|
+
Args:
|
|
413
|
+
method: 方法对象
|
|
414
|
+
args: 位置参数
|
|
415
|
+
kwargs: 关键字参数
|
|
416
|
+
|
|
417
|
+
Returns:
|
|
418
|
+
参数字典
|
|
419
|
+
"""
|
|
420
|
+
params = {}
|
|
421
|
+
|
|
422
|
+
# 获取方法签名
|
|
423
|
+
sig = inspect.signature(method)
|
|
424
|
+
parameters = list(sig.parameters.values())
|
|
425
|
+
try:
|
|
426
|
+
type_hints = get_type_hints(method, include_extras=True)
|
|
427
|
+
except (NameError, TypeError):
|
|
428
|
+
type_hints = {}
|
|
429
|
+
|
|
430
|
+
# 处理位置参数,跳过第一个参数self(MapperProxy调用时不传递self)
|
|
431
|
+
for i, arg in enumerate(args):
|
|
432
|
+
param_index = i + 1 # 跳过self
|
|
433
|
+
if param_index < len(parameters):
|
|
434
|
+
parameter = parameters[param_index]
|
|
435
|
+
annotation = type_hints.get(parameter.name, parameter.annotation)
|
|
436
|
+
alias = self._parameter_alias(annotation, parameter.name)
|
|
437
|
+
self._add_param(params, alias, arg)
|
|
438
|
+
if alias != parameter.name:
|
|
439
|
+
params.setdefault(parameter.name, arg)
|
|
440
|
+
|
|
441
|
+
# 处理关键字参数
|
|
442
|
+
for key, value in kwargs.items():
|
|
443
|
+
parameter = sig.parameters.get(key)
|
|
444
|
+
annotation = type_hints.get(key, parameter.annotation) if parameter else None
|
|
445
|
+
alias = self._parameter_alias(annotation, key)
|
|
446
|
+
self._add_param(params, alias, value)
|
|
447
|
+
if alias != key:
|
|
448
|
+
params.setdefault(key, value)
|
|
449
|
+
|
|
450
|
+
return params
|
|
451
|
+
|
|
452
|
+
@staticmethod
|
|
453
|
+
def _parameter_alias(annotation: Any, default: str) -> str:
|
|
454
|
+
if isinstance(annotation, Param):
|
|
455
|
+
return annotation.value
|
|
456
|
+
if get_origin(annotation) is Annotated:
|
|
457
|
+
for metadata in get_args(annotation)[1:]:
|
|
458
|
+
if isinstance(metadata, Param):
|
|
459
|
+
return metadata.value
|
|
460
|
+
return default
|
|
461
|
+
|
|
462
|
+
@staticmethod
|
|
463
|
+
def _add_param(params: Dict[str, Any], name: str, value: Any) -> None:
|
|
464
|
+
"""保留命名参数,同时为单对象参数提供可预测的字段展开。"""
|
|
465
|
+
params[name] = value
|
|
466
|
+
|
|
467
|
+
expanded = None
|
|
468
|
+
if isinstance(value, Mapping):
|
|
469
|
+
expanded = value
|
|
470
|
+
elif is_dataclass(value) and not isinstance(value, type):
|
|
471
|
+
expanded = asdict(value)
|
|
472
|
+
elif hasattr(value, 'to_dict') and callable(value.to_dict):
|
|
473
|
+
candidate = value.to_dict()
|
|
474
|
+
if isinstance(candidate, Mapping):
|
|
475
|
+
expanded = candidate
|
|
476
|
+
elif hasattr(value, '__dict__'):
|
|
477
|
+
expanded = {
|
|
478
|
+
key: item for key, item in vars(value).items()
|
|
479
|
+
if not key.startswith('_')
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if expanded:
|
|
483
|
+
for key, item in expanded.items():
|
|
484
|
+
params.setdefault(str(key), item)
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
class MapperRegistry:
|
|
488
|
+
"""
|
|
489
|
+
Mapper注册中心
|
|
490
|
+
|
|
491
|
+
管理所有Mapper接口,支持懒加载
|
|
492
|
+
"""
|
|
493
|
+
|
|
494
|
+
def __init__(self):
|
|
495
|
+
"""初始化Mapper注册中心"""
|
|
496
|
+
self.mappers: Dict[str, Type] = {}
|
|
497
|
+
|
|
498
|
+
def add_mapper(self, mapper_class: Type) -> None:
|
|
499
|
+
"""
|
|
500
|
+
添加Mapper
|
|
501
|
+
|
|
502
|
+
Args:
|
|
503
|
+
mapper_class: Mapper类
|
|
504
|
+
"""
|
|
505
|
+
key = f"{mapper_class.__module__}.{mapper_class.__name__}"
|
|
506
|
+
self.mappers[key] = mapper_class
|
|
507
|
+
|
|
508
|
+
def get_mapper(self, mapper_class: Type) -> Optional[Type]:
|
|
509
|
+
"""
|
|
510
|
+
获取Mapper类
|
|
511
|
+
|
|
512
|
+
Args:
|
|
513
|
+
mapper_class: Mapper类
|
|
514
|
+
|
|
515
|
+
Returns:
|
|
516
|
+
Mapper类,未找到返回None
|
|
517
|
+
"""
|
|
518
|
+
key = f"{mapper_class.__module__}.{mapper_class.__name__}"
|
|
519
|
+
return self.mappers.get(key)
|
|
520
|
+
|
|
521
|
+
def has_mapper(self, mapper_class: Type) -> bool:
|
|
522
|
+
"""
|
|
523
|
+
检查Mapper是否已注册
|
|
524
|
+
|
|
525
|
+
Args:
|
|
526
|
+
mapper_class: Mapper类
|
|
527
|
+
|
|
528
|
+
Returns:
|
|
529
|
+
是否已注册
|
|
530
|
+
"""
|
|
531
|
+
key = f"{mapper_class.__module__}.{mapper_class.__name__}"
|
|
532
|
+
return key in self.mappers
|
|
533
|
+
|
|
534
|
+
def get_all_mappers(self) -> Dict[str, Type]:
|
|
535
|
+
"""获取所有Mapper"""
|
|
536
|
+
return self.mappers
|
|
537
|
+
|
|
538
|
+
def clear(self) -> None:
|
|
539
|
+
"""清空所有Mapper"""
|
|
540
|
+
self.mappers.clear()
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyMyBatis监控指标模块
|
|
3
|
+
|
|
4
|
+
提供Prometheus兼容的指标收集和导出功能
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .metrics import (
|
|
8
|
+
MetricsCollector,
|
|
9
|
+
Counter,
|
|
10
|
+
Gauge,
|
|
11
|
+
Histogram,
|
|
12
|
+
Timer,
|
|
13
|
+
get_default_collector,
|
|
14
|
+
QUERY_COUNTER,
|
|
15
|
+
QUERY_TIMER,
|
|
16
|
+
ACTIVE_CONNECTIONS,
|
|
17
|
+
IDLE_CONNECTIONS,
|
|
18
|
+
CACHE_HIT_COUNTER,
|
|
19
|
+
CACHE_MISS_COUNTER,
|
|
20
|
+
TRANSACTION_COUNTER,
|
|
21
|
+
CIRCUIT_BREAKER_STATE,
|
|
22
|
+
CIRCUIT_BREAKER_FAILURE_RATE
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
'MetricsCollector',
|
|
27
|
+
'Counter',
|
|
28
|
+
'Gauge',
|
|
29
|
+
'Histogram',
|
|
30
|
+
'Timer',
|
|
31
|
+
'get_default_collector',
|
|
32
|
+
'QUERY_COUNTER',
|
|
33
|
+
'QUERY_TIMER',
|
|
34
|
+
'ACTIVE_CONNECTIONS',
|
|
35
|
+
'IDLE_CONNECTIONS',
|
|
36
|
+
'CACHE_HIT_COUNTER',
|
|
37
|
+
'CACHE_MISS_COUNTER',
|
|
38
|
+
'TRANSACTION_COUNTER',
|
|
39
|
+
'CIRCUIT_BREAKER_STATE',
|
|
40
|
+
'CIRCUIT_BREAKER_FAILURE_RATE'
|
|
41
|
+
]
|