触发器 #
一、触发器概述 #
1.1 什么是触发器 #
触发器是在特定事件发生时自动执行的代码:
text
触发器特点:
├── 自动触发执行
├── 响应数据变更事件
├── 实现业务规则
├── 审计日志记录
└── 数据验证
1.2 触发器类型 #
| 类型 | 说明 |
|---|---|
| BEFORE | 事件发生前执行 |
| AFTER | 事件发生后执行 |
1.3 支持的事件 #
| 事件 | 说明 |
|---|---|
| CREATE | 创建记录时 |
| READ | 读取记录时 |
| UPDATE | 更新记录时 |
| DELETE | 删除记录时 |
二、Hook机制 #
2.1 什么是Hook #
Hook是OrientDB的事件钩子机制:
text
Hook类型:
├── Database Hook - 数据库级别
├── Class Hook - 类级别
└── Record Hook - 记录级别
2.2 创建Hook类 #
Java实现:
java
import com.orientechnologies.orient.core.hook.ODocumentHookAbstract;
import com.orientechnologies.orient.core.record.impl.ODocument;
public class PersonHook extends ODocumentHookAbstract {
@Override
public DISTRIBUTED_EXECUTION_MODE getDistributedExecutionMode() {
return DISTRIBUTED_EXECUTION_MODE.SOURCE_NODE;
}
@Override
public void onRecordBeforeCreate(ODocument iDocument) {
if (iDocument.getClassName().equals("Person")) {
iDocument.field("createdAt", new Date());
iDocument.field("updatedAt", new Date());
}
}
@Override
public void onRecordAfterCreate(ODocument iDocument) {
System.out.println("Person created: " + iDocument.field("name"));
}
@Override
public void onRecordBeforeUpdate(ODocument iDocument) {
if (iDocument.getClassName().equals("Person")) {
iDocument.field("updatedAt", new Date());
}
}
}
2.3 注册Hook #
java
ODatabaseDocument db = orientDB.open("mydb", "admin", "admin");
db.registerHook(new PersonHook());
三、SQL触发器 #
3.1 创建触发器类 #
sql
CREATE CLASS PersonTrigger EXTENDS OTriggered
3.2 定义触发器方法 #
sql
CREATE FUNCTION PersonTrigger.onAfterCreate() "
var db = orient.getDatabase();
var record = doc;
db.command('INSERT INTO AuditLog SET action = ?, recordId = ?, timestamp = sysdate()',
['CREATE', record.rid.toString()]);
return record;
" LANGUAGE Javascript
3.3 绑定触发器 #
sql
ALTER CLASS Person CUSTOM onAfterCreate = PersonTrigger.onAfterCreate
3.4 完整示例 #
sql
CREATE CLASS PersonTrigger EXTENDS OTriggered
CREATE FUNCTION PersonTrigger.onBeforeCreate() "
var db = orient.getDatabase();
var record = doc;
if (!record.name || record.name.trim() === '') {
throw new Error('Name is required');
}
record.createdAt = new Date();
record.updatedAt = new Date();
return record;
" LANGUAGE Javascript
ALTER CLASS Person CUSTOM onBeforeCreate = PersonTrigger.onBeforeCreate
四、触发器事件 #
4.1 onBeforeCreate #
创建记录前触发:
javascript
// 函数名: PersonTrigger.onBeforeCreate
var record = doc;
if (!record.email || !record.email.includes('@')) {
throw new Error('Valid email is required');
}
record.createdAt = new Date();
record.status = record.status || 'active';
return record;
4.2 onAfterCreate #
创建记录后触发:
javascript
// 函数名: PersonTrigger.onAfterCreate
var db = orient.getDatabase();
var record = doc;
db.command('INSERT INTO AuditLog SET action = ?, entityType = ?, entityId = ?, timestamp = sysdate()',
['CREATE', 'Person', record.rid.toString()]);
if (record.email) {
db.command('INSERT INTO Notification SET type = ?, recipient = ?, message = ?, createdAt = sysdate()',
['welcome', record.email, 'Welcome to our platform!']);
}
return record;
4.3 onBeforeUpdate #
更新记录前触发:
javascript
// 函数名: PersonTrigger.onBeforeUpdate
var record = doc;
record.updatedAt = new Date();
if (record.email && !record.email.includes('@')) {
throw new Error('Valid email is required');
}
return record;
4.4 onAfterUpdate #
更新记录后触发:
javascript
// 函数名: PersonTrigger.onAfterUpdate
var db = orient.getDatabase();
var record = doc;
db.command('INSERT INTO AuditLog SET action = ?, entityType = ?, entityId = ?, timestamp = sysdate()',
['UPDATE', 'Person', record.rid.toString()]);
return record;
4.5 onBeforeDelete #
删除记录前触发:
javascript
// 函数名: PersonTrigger.onBeforeDelete
var db = orient.getDatabase();
var record = doc;
var orders = db.query('SELECT COUNT(*) FROM Order WHERE customer = ?', [record.rid]);
if (orders[0].COUNT > 0) {
throw new Error('Cannot delete person with existing orders');
}
return record;
4.6 onAfterDelete #
删除记录后触发:
javascript
// 函数名: PersonTrigger.onAfterDelete
var db = orient.getDatabase();
var record = doc;
db.command('INSERT INTO AuditLog SET action = ?, entityType = ?, entityId = ?, timestamp = sysdate()',
['DELETE', 'Person', record.rid.toString()]);
return record;
五、实际应用示例 #
5.1 审计日志 #
javascript
// 函数名: AuditTrigger.onAfterCreate
var db = orient.getDatabase();
var record = doc;
db.command('INSERT INTO AuditLog SET ' +
'action = ?, ' +
'entityType = ?, ' +
'entityId = ?, ' +
'userId = ?, ' +
'timestamp = sysdate(), ' +
'newValue = ?',
[
'CREATE',
record.getClassName(),
record.rid.toString(),
currentUser,
JSON.stringify(record.toJSON())
]);
return record;
5.2 数据验证 #
javascript
// 函数名: OrderTrigger.onBeforeCreate
var record = doc;
if (!record.customer) {
throw new Error('Customer is required');
}
if (!record.items || record.items.length === 0) {
throw new Error('At least one item is required');
}
var totalAmount = 0;
for (var i = 0; i < record.items.length; i++) {
var item = record.items[i];
if (!item.productId || !item.quantity || item.quantity <= 0) {
throw new Error('Invalid item data');
}
totalAmount += item.quantity * item.price;
}
record.totalAmount = totalAmount;
record.status = 'pending';
record.createdAt = new Date();
return record;
5.3 自动计算字段 #
javascript
// 函数名: OrderItemTrigger.onAfterCreate
var db = orient.getDatabase();
var record = doc;
var product = db.query('SELECT FROM Product WHERE @rid = ?', [record.productId])[0];
db.command('UPDATE Order SET totalAmount = (SELECT SUM(quantity * price) FROM OrderItem WHERE orderId = ?) WHERE @rid = ?',
[record.orderId, record.orderId]);
db.command('UPDATE Product SET stock = stock - ? WHERE @rid = ?',
[record.quantity, record.productId]);
return record;
5.4 级联更新 #
javascript
// 函数名: DepartmentTrigger.onAfterUpdate
var db = orient.getDatabase();
var record = doc;
if (record.oldValue && record.oldValue.name !== record.name) {
db.command('UPDATE Employee SET departmentName = ? WHERE department = ?',
[record.name, record.rid]);
}
return record;
5.5 软删除 #
javascript
// 函数名: SoftDeleteTrigger.onBeforeDelete
var db = orient.getDatabase();
var record = doc;
db.command('UPDATE ? SET deletedAt = sysdate(), isDeleted = true WHERE @rid = ?',
[record.getClassName(), record.rid]);
throw new Error('SOFT_DELETE');
六、触发器管理 #
6.1 查看触发器 #
sql
SELECT FROM OTriggered
SELECT name, code FROM OFunction WHERE name LIKE '%Trigger%'
6.2 禁用触发器 #
sql
ALTER CLASS Person CUSTOM onBeforeCreate = NULL
ALTER CLASS Person CUSTOM onAfterCreate = NULL
6.3 删除触发器 #
sql
DROP FUNCTION PersonTrigger.onBeforeCreate
DROP CLASS PersonTrigger
七、触发器最佳实践 #
7.1 设计原则 #
text
触发器设计原则:
├── 保持触发器简单
├── 避免递归触发
├── 处理异常情况
├── 记录触发器日志
└── 测试触发器逻辑
7.2 性能考虑 #
text
性能优化建议:
├── 避免复杂计算
├── 减少数据库操作
├── 使用批量操作
├── 避免循环触发
└── 合理使用事务
7.3 错误处理 #
javascript
// 函数名: SafeTrigger.onAfterCreate
var db = orient.getDatabase();
var record = doc;
try {
db.command('INSERT INTO AuditLog SET action = ?, entityId = ?, timestamp = sysdate()',
['CREATE', record.rid.toString()]);
} catch (e) {
db.command('INSERT INTO ErrorLog SET error = ?, timestamp = sysdate()',
[e.message]);
}
return record;
八、触发器调试 #
8.1 日志记录 #
javascript
// 函数名: DebugTrigger.onAfterCreate
var record = doc;
print('Trigger fired for: ' + record.getClassName());
print('Record ID: ' + record.rid);
print('Record data: ' + JSON.stringify(record.toJSON()));
return record;
8.2 调试模式 #
javascript
// 函数名: DebugTrigger.onAfterCreate
var record = doc;
var debug = true;
if (debug) {
var db = orient.getDatabase();
db.command('INSERT INTO DebugLog SET message = ?, data = ?, timestamp = sysdate()',
['Trigger executed', JSON.stringify(record.toJSON())]);
}
return record;
九、总结 #
触发器要点:
| 操作 | 语法 | 说明 |
|---|---|---|
| 创建触发器类 | CREATE CLASS … EXTENDS OTriggered | 创建触发器类 |
| 定义触发器方法 | CREATE FUNCTION | 定义触发逻辑 |
| 绑定触发器 | ALTER CLASS CUSTOM | 绑定到类 |
| 禁用触发器 | ALTER CLASS CUSTOM = NULL | 禁用触发器 |
下一步,让我们学习用户权限管理!
最后更新:2026-03-27