# 批处理操作

OData V4 支持批处理请求，将多个操作合并为一次 HTTP 请求，减少网络往返次数，并通过变更集（Changeset）支持原子事务。

## 目录

- [概述](#概述)
- [创建批处理](#创建批处理)
- [批处理操作类型](#批处理操作类型)
- [变更集（原子事务）](#变更集原子事务)
- [执行批处理](#执行批处理)
- [处理结果](#处理结果)
- [最佳实践](#最佳实践)

## 概述

批处理请求的优势：

- 将多个操作合并为一次 HTTP 请求
- 降低网络延迟
- 通过变更集将操作分组为原子事务（全部成功或全部失败）
- 可混合读写操作

## 创建批处理

```python
batch = client.create_batch()
```

## 批处理操作类型

### GET 操作

```python
batch = client.create_batch()

# 将 GET 操作加入队列
get_op1 = batch.get("Products", 1)
get_op2 = batch.get("Products", 2)
get_op3 = batch.get("Customers", "ALFKI")

# 执行并检查结果
response = await client.execute_batch_async(batch)

for result in response.results:
    print(f"op={result.operation_id}  status={result.status_code}  body={result.response_body[:80]}")
```

### CREATE 操作

```python
batch = client.create_batch()

create_op = batch.create("Products", {"Name": "新组件", "Price": 29.99})

response = await client.execute_batch_async(batch)

for r in response.results:
    if r.is_success:
        print(f"已创建：{r.response_body}")
```

### UPDATE 操作

```python
batch = client.create_batch()

# PATCH
batch.update("Products", 123, {"Price": 39.99})

# 带 ETag 的 PATCH（并发控制）
batch.update("Products", 456, {"Price": 49.99}, etag='W/"abc123"')

response = await client.execute_batch_async(batch)
```

### DELETE 操作

```python
batch = client.create_batch()

batch.delete("Products", 123)
batch.delete("Products", 456, etag='W/"xyz789"')

response = await client.execute_batch_async(batch)
```

## 变更集（原子事务）

变更集将写操作分组，组内所有操作要么全部成功，要么全部回滚。

```python
batch = client.create_batch()

# 创建变更集
changeset = batch.create_changeset()

# 变更集内的所有操作是原子的
changeset.create("Products", {"Name": "组件 A"})
changeset.update("Products", 100, {"InStock": False})
changeset.delete("Products", 200)

# 执行——变更集内的操作作为一个整体提交或回滚
response = await client.execute_batch_async(batch)

print(f"全部成功：{response.all_succeeded}")
```

### 混合读操作和变更集

```python
batch = client.create_batch()

# 读操作（不在变更集内，非原子）
batch.get("Products", 1)

# 原子变更集
changeset = batch.create_changeset()
changeset.create("Products", {"Name": "新产品"})
changeset.update("Products", 2, {"Price": 10.0})

# 另一个读操作（不在变更集内）
batch.get("Customers", "ALFKI")

response = await client.execute_batch_async(batch)
```

### 多个变更集

```python
batch = client.create_batch()

# 第一个变更集（独立）
cs1 = batch.create_changeset()
cs1.create("Products", {"Name": "产品 A"})
cs1.create("Products", {"Name": "产品 B"})

# 第二个变更集（独立）
cs2 = batch.create_changeset()
cs2.update("Customers", "ALFKI", {"City": "北京"})
cs2.update("Customers", "ANATR", {"City": "上海"})

response = await client.execute_batch_async(batch)
```

## 执行批处理

```python
response = await client.execute_batch_async(batch)
```

## 处理结果

### 检查整体成功状态

```python
response = await client.execute_batch_async(batch)

if not response.all_succeeded:
    for r in response.results:
        if not r.is_success:
            print(f"操作 {r.operation_id} 失败：{r.status_code} — {r.error_message}")
```

### 检查单个操作结果

```python
batch = client.create_batch()
batch.get("Products", 1)
batch.create("Products", {"Name": "测试"})

response = await client.execute_batch_async(batch)

for r in response.results:
    print(f"id={r.operation_id}  status={r.status_code}  success={r.is_success}")
    if r.response_body:
        print(f"  响应体={r.response_body[:120]}")
    if r.error_message:
        print(f"  错误={r.error_message}")
```

## 最佳实践

1. **关联操作放入变更集** —— 若操作之间有依赖关系，将它们放在同一变更集中
2. **控制批处理规模** —— 过大的批处理可能超时或被服务器拒绝
3. **处理局部失败** —— 变更集外的操作可能独立失败
4. **了解服务器限制** —— 部分服务器对批处理大小或嵌套深度有限制
