触发器 #

一、触发器概述 #

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