连接钩子:在连接和断开与数据库的连接之前或之后立即执行函数。 日志钩子:在不同的日志级别下将消息记录到stdout。 生命周期钩子:在对数据库进行调用之前或之后立即执行函数。
Sequelize ORM提供了许多钩子来管理数据库事务的整个生命周期。
beforeCreate生命周期钩子可用于在创建新用户之前对密码进行散列处理:
User.beforeCreate(async (user, options) => {const hashedPassword = await hashPassword(user.password);user.password = hashedPassword;});
接下来,我使用afterConnect连接钩子来设置会话参数。
通过这个YugabyteDB部署,您可以从跟随者执行读取操作以减少延迟,并且无需从主集群节点读取:
const config = {host: process.env.DB_HOST,port: 5433,dialect: "postgres",dialectOptions: {ssl: {require: true,rejectUnauthorized: true,ca: [CERTIFICATE],},},pool: {max: 5,min: 1,acquire: 30000,idle: 10000,},hooks: {async afterConnect(connection) {if (process.env.DB_DEPLOYMENT_TYPE === "multi_region_with_read_replicas") {await connection.query("set yb_read_from_followers = true; set session characteristics as transaction read only;");}},},};const connection = new Sequelize(process.env.DATABASE_NAME,process.env.DATABASE_USER,process.env.DATABASE_PASSWORD,config);
通过使用这个钩子,在连接池中的每个数据库会话在建立新连接时都会设置这些参数:
set yb_read_from_followers = true;:此参数控制是否启用从跟随者读取。 set session characteristics as transaction read only;:此参数将只读设置应用于后续的所有语句和事务块。
尽管在Node.js社区中,Prisma是许多人首选的ORM,但在撰写本文时,Prisma并不包含Sequelize中的许多内置钩子。目前,该库包含用于处理查询生命周期、日志记录和断开连接的钩子,但在建立连接之前或之后提供的帮助很少。
以下是如何使用Prisma的生命周期中间件在创建用户之前对密码进行哈希处理的方法:
prisma.$use(async (params, next) => {if (params.model == 'User' && params.action == 'create') {params.args.data.password = await hashPassword(params.args.data.password);}return next(params)})const create = await prisma.user.create({data: {username: 'bhoyer',password: 'abc123'},})
要设置会话参数以利用我们的读取副本,我们需要在查询数据库之前执行一条语句:
await prisma.$executeRaw(`set yb_read_from_followers = true; set session characteristics as transaction read only;`);const users = await prisma.user.findMany();
如果您需要立即在连接池中建立连接以设置参数,您可以使用Prisma显式地进行连接,而不使用连接池的延迟连接。
Prisma具有查询(query)、错误(error)、信息(info)和警告(warn)等日志级别。可以使用基于事件的日志记录来处理查询事件:
const prisma = new PrismaClient({log: [{emit: 'event',level: 'query',},{emit: 'stdout',level: 'error',},{emit: 'stdout',level: 'info',},{emit: 'stdout',level: 'warn',},],});prisma.$on('query', (e) => {console.log('Query: ' + e.query);console.log('Params: ' + e.params);console.log('Duration: ' + e.duration + 'ms');});
const prisma = new PrismaClient();prisma.$on('beforeExit', async () => {PrismaClient still availableawait prisma.issue.create({data: {message: 'Connection exiting.'},})});
要对密码进行哈希处理,可以使用自定义函数来手动处理:
async function handlePassword(password) {const hashedPassword = await hashPassword(password);return hashedPassword;}const password = await handlePassword(params.password);knex('users').insert({...params, password});
在Knex.js查询构建器中实现连接钩子所需的语法与Sequelize类似。以下是如何设置会话参数以从YugabyteDB的副本节点读取的示例代码:
const knex = require('knex')({client: 'pg',connection: {/*...*/},pool: {afterCreate: function (connection, done) {connection.query('set yb_read_from_followers = true; set session characteristics as transaction read only;', function (err) {if (err) {//Query faileddone(err, conn);} else {console.log("Reading from replicas.");done();}});}}});
const config = {user: process.env.DB_USER,host: process.env.DB_HOST,password: process.env.DB_PASSWORD,port: 5433,database: process.env.DB_NAME,min: 1,max: 10,idleTimeoutMillis: 5000,connectionTimeoutMillis: 5000,ssl: {rejectUnauthorized: true,ca: [CERTIFICATE],servername: process.env.DB_HOST,}};const pool = new Pool(config);pool.on("connect", (c) => {c.query("set yb_read_from_followers = true; set session characteristics as transaction read only;");});pool.on("error", (e) => {console.log("Connection error: ", e);});
async function handlePassword(password) {const hashedPassword = await hashPassword(password);return hashedPassword;}const password = await handlePassword(params.password);const user = await pool.query('INSERT INTO user(username, password) VALUES ($1, $2) RETURNING *', [params.username, password]);
正如你所看到的,钩子可以解决之前由复杂且容易出错的应用程序代码所引起的许多问题。每个应用程序都有不同的要求和面临新的挑战。在你的开发过程中,可能会经过很多年才需要使用特定的钩子,但现在,当那一天来临时,请准备就绪。
文章转载自云原生数据库,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




