Improve transaction management and connection cleanup in incremental sync

- Add explicit BEGIN TRANSACTION for clear transaction boundaries
- Ensure IDENTITY_INSERT state is properly tracked and cleaned up
- Add finally block to guarantee Access connection cleanup
- Move table existence check inside sync loop for better scope

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Misaka_Company
2026-06-11 15:33:57 +08:00
parent 11d284a6bf
commit 3791966446

View File

@@ -113,6 +113,7 @@ def process_sync_task():
log_error(f"无法访问文件: {clean_path}")
continue
acc_conn = None
try:
acc_conn = db_utils.get_access_conn(clean_path)
acc_cursor = acc_conn.cursor()
@@ -139,7 +140,25 @@ def process_sync_task():
log_processing(f"正在同步表 [{acc_table}] → [{target_table}] ({len(record_ids)} 条记录)")
# 检查目标表是否存在,不存在则自动创建
if not db_utils.table_exists(sql_cursor, target_schema, target_table):
db_utils.ensure_schema(sql_cursor, target_schema)
acc_cursor.execute(f"SELECT TOP 1 * FROM [{acc_table}]")
db_utils.create_table_from_access(
sql_cursor, target_schema, target_table,
acc_cursor.description, pk_col
)
log_info(f"目标表 [{target_table}] 不存在,已自动创建")
# 事务状态追踪
identity_enabled = False
transaction_begun = False
try:
# --- 开始事务 ---
sql_cursor.execute("BEGIN TRANSACTION")
transaction_begun = True
# --- A. Access 查新数据 ---
ids_placeholders = ','.join(['?'] * len(record_ids))
acc_sql = f"SELECT * FROM [{acc_table}] WHERE [{pk_col}] IN ({ids_placeholders})"
@@ -147,35 +166,28 @@ def process_sync_task():
new_rows = acc_cursor.fetchall()
acc_cols = [col[0] for col in acc_cursor.description]
# --- B. SQL Server 删旧插新 (事务) ---
# 1. 删除
# --- B. SQL Server 删旧插新 ---
# 1. 删除旧数据
del_sql = f"DELETE FROM {target_full_name} WHERE [{pk_col}] IN ({ids_placeholders})"
sql_cursor.execute(del_sql, record_ids)
# 2. 插入
# 2. 插入新数据
if new_rows:
insert_sql = db_utils.generate_insert_sql(target_schema, target_table, acc_cols)
# 检查并启用 IDENTITY_INSERT
has_identity = has_identity_column(sql_cursor, target_schema, target_table)
identity_enabled = False
if has_identity:
try:
sql_cursor.execute(f"SET IDENTITY_INSERT {target_full_name} ON")
identity_enabled = True
except Exception as id_err:
log_error(f"无法启用 IDENTITY_INSERT: {id_err}")
raise
sql_cursor.executemany(insert_sql, new_rows)
# 关闭 IDENTITY_INSERT
if identity_enabled:
try:
sql_cursor.execute(f"SET IDENTITY_INSERT {target_full_name} OFF")
except Exception as id_err:
log_warning(f"关闭 IDENTITY_INSERT 时警告: {id_err}")
identity_enabled = False
# 3. 标记日志 Synced = 1
log_placeholders = ','.join(['?'] * len(log_ids))
@@ -186,23 +198,38 @@ def process_sync_task():
"""
sql_cursor.execute(update_log_sql, log_ids)
# 提交事务
sql_conn.commit()
transaction_begun = False
log_info(f"表 [{target_table}] 同步完成: {len(record_ids)} 条记录")
file_success_count += 1
file_total_records += len(record_ids)
except Exception as tbl_err:
# 确保清理 IDENTITY_INSERT 状态
# 回滚事务
if transaction_begun:
sql_conn.rollback()
transaction_begun = False
# 清理 IDENTITY_INSERT 状态
if identity_enabled:
try:
sql_cursor.execute(f"SET IDENTITY_INSERT {target_full_name} OFF")
except:
pass
log_error(f"表 [{acc_table}] 同步失败: {tbl_err}")
sql_conn.rollback()
file_error_count += 1
acc_conn.close() # 关闭 Access 连接
finally:
# 确保 Access 连接关闭
try:
if acc_conn:
acc_conn.close()
log_info(f"已关闭 Access 连接: {os.path.basename(clean_path)}")
except Exception as close_err:
log_warning(f"关闭 Access 连接时出错: {close_err}")
# 输出文件级别的汇总
if file_success_count > 0 or file_error_count > 0: