各种主流数据库,都集成了触发器的功能。触发器提供了一种机制,允许开发者在对数据库表的插入、更新、删除的前后捕获相应的数据行。从而针对数据行实现特定的逻辑。结合具体的例子来看,比如有多个应用系统都对数据库某张表进行了插入操作,现在需要对该表的某个字段进行修改。我们可以怎么做呢,如果是倾向于在应用系统中改变逻辑从而实现目标,那么我们就需要同时修改两套应用系统;而如果是倾向于使数据库的话我们只需要针对这个表的相应触发器进行操作即可。显然两者都能达到目的,但是实际的应用场景并不总是如我们所愿。比如应用系统是用c#开发的但是不擅长c#的话就不好轻易的去改动代码,这个时候刚好会数据库,那么触发器就可以派上用场了。还有一点就是我们不知道某个表会被哪些哪些应用系统操作的时候,我们也可以使用触发器来解决问题。总而言之,触发器守住了数据的入口,在某种程度上也提供了一定的便利。
**触发器类似于事件,创建触发器就是注册事件回调过程的过程**。我们知道,一般事件是被动触发的,而且事件可以形成事件链,触发器也是如此。对于事件一般我们是通过"注册回调函数"这种方式来完成,触发器也是一种"回调函数",只不过这种"回调函数"使用的是数据库支持的语法。一般回调函数都会传入 上下文的变量,我们知道在触发器里有old和new变量,这两个变量就是上下文变量,用来指代更新前后的数据行。
触发器分为插入触发器、更新触发器、删除触发器。这三个触发器都有前置和后置两个动作,也就是插入之前/之后,更新之前/之后,删除之前/之后,对应语法就是BEFORE 和AFTER。
下面通过mysql触发器的示例来介绍它的使用方法。首先我们创建一张测试表,后面的插入触发器、更新触发器和删除触发器都围绕这张表来说明。
DROP TABLE app_user;
CREATE TABLE app_user(id int primary key AUTO_INCREMENT,username varchar(32),pwd varchar(32));
以下代码为表app_user创建了一个前置插入触发器,该触发器的作用将插入到app_user表的数据同步到app_user2表,app_user2表与app_user表具有相同的表结构
CREATE TABLE IF NOT EXISTS app_user2 like app_user;
DROP TRIGGER IF EXISTS trigger_before_insert_app_user;
CREATE TRIGGER trigger_before_insert_app_user BEFORE INSERT ON app_user FOR EACH ROW BEGIN
insert into app_user2(`id`,`username`,`pwd`) values(new.`id`,new.`username`,new.`pwd`);
END;
使用INSERT INTO往app_user表插入一条数据后之后查看两表数据,可以看到app_user表数据行插入到了app_user2
以下代码为app_user表创建了一个前置更新触发器,该触发器的作用在更新app_user表的同时将app_user2表的记录同步更新
DROP TRIGGER IF EXISTS trigger_before_update_app_user;
CREATE TRIGGER trigger_before_update_app_user BEFORE UPDATE ON app_user FOR EACH ROW BEGIN
UPDATE app_user2 set
`id` = new.`id`,
`username` = new.`username`,
`pwd` = new.`pwd`
WHERE id=old.id;
END;
**使用UPDATE语句更新app_user表中id=‘1’的记录中字段username值由’test’更新为’test1’ **
可以看到app_user2表记录数据得到了同步更新
字段username更新前的值是’test’,更新后变成了’test1’,更新之前的值可以通过old变量访问,更新之后的值通过new变量访问,
可以通过微调触发器代码来查看数据,将old和new变量中的username字段进行了拼接并更新到username字段
然后重新创建更新触发器
DROP TRIGGER IF EXISTS trigger_before_update_app_user;
CREATE TRIGGER trigger_before_update_app_user BEFORE UPDATE ON app_user FOR EACH ROW BEGIN
UPDATE app_user2 set
`id` = new.`id`,
`username` = concat('old.username:',old.username,' new.username:',new.`username`),
`pwd` = new.`pwd`
WHERE id=old.id;
END;
执行UPDATE语句后可以对比查看old.username和new.username变量的值
以下代码为app_user表创建了一个前置删除触发器,删除app_user表记录的同时同步删除app_user2表记录
DROP TRIGGER IF EXISTS trigger_before_delete_app_user;
CREATE TRIGGER trigger_before_delete_app_user BEFORE DELETE ON app_user FOR EACH ROW BEGIN
DELETE FROM app_user2 WHERE id=old.id;
END;
使用show triggers命令查看所有触发器
也可以指定关键字进行模糊查询
触发器作为一种特定的"回调函数",它在执行的过程中也会抛出异常。一般编程语言中都有类似的try catch finally来进行异常捕获,
当然mysql也有对应的异常处理机制
出错继续
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET @x = 0;
出错退出
DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @x = 0;
代码段中 SET @x = 0 是出错时的处理程序,这样判断程序是否出错就可以根据x变量的值来判断
我们知道 往主键唯一的表插入重复记录会抛出异常
app_user表 “前置插入触发器” 当前逻辑为
DROP TRIGGER IF EXISTS trigger_before_insert_app_user;
CREATE TRIGGER trigger_before_insert_app_user BEFORE INSERT ON app_user FOR EACH ROW BEGIN
insert into app_user2(`id`,`username`,`pwd`) values(new.`id`,new.`username`,new.`pwd`);
END;
在不改动逻辑的情况下,插入重复记录的时候,会出现报错
修改触发器模拟报错场景,比如将insert语句中的id写死为1 ,这样无论往app_user表中插入任何数据都会引发报错
那么如何使语句 INSERT INTO app_user(id,username,pwd) values(2,‘test’,‘123’); 成功执行呢
可以修改触发器如下
DROP TRIGGER IF EXISTS trigger_before_insert_app_user;
CREATE TRIGGER trigger_before_insert_app_user BEFORE INSERT ON app_user FOR EACH ROW BEGIN
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET @x = 0;
insert into app_user2(`id`,`username`,`pwd`) values(1,new.`username`,new.`pwd`);
END;
可以看到app_user表有2条记录,app_user表只有1条记录
1.使用触发器可以实现表数据的同步,可以用来实现增量数据同步。
2.不管数据是否插入、更新、删除成功,前置触发器都会首先被调用。
3.触发器中有异常也是能够被捕获的。