Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

可否来一发typeorm事务demo? #34

Closed
silent-tan opened this issue Jun 5, 2019 · 7 comments
Closed

可否来一发typeorm事务demo? #34

silent-tan opened this issue Jun 5, 2019 · 7 comments

Comments

@silent-tan
Copy link

silent-tan commented Jun 5, 2019

async create(createPostDto: CreatePostDto): Promise<PostEntity> {
    return await getManager().transaction(async manager => {
      const defaultCover = `https://images.unsplash.com/
      photo-1546623235-23f145a669ef?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=900&h=600&fit=crop&ixid=eyJhcHBfaWQiOjF9`;
      const postDto: PostDto = {
        title: createPostDto.title,
        uid: createPostDto.uid,
        content: createPostDto.content,
        type: createPostDto.type,
        status: createPostDto.status,
        cover: createPostDto.cover || defaultCover,
        subTitle: createPostDto.subTitle || '',
        description: createPostDto.description || '',
      };
      // 保存文章
      const post = await manager.save<PostEntity>(
        manager.create<PostEntity>(PostEntity, postDto),
      );
      const postId = post.id;

      // ------ 文章分类表
      const cids = createPostDto.cids || [];
      if (cids.length === 0) {
        // 默认未分类
        await this.cateRelationService.create({
          cid: 14, // 未分类,
          composeId: postId,
          composeType: SourceType.POST,
        });
      } else {
        const cates = await Promise.all(
          _map(cids, cid => {
            return this.cateService.findOne(cid);
          }),
        );
        // 过滤不存在的分类
        const existCates = _filter(cates, item => !_isUndefined(item));
        // 批量插入分类文章映射
        // TODO 待优化
        const cateRaltions = await Promise.all(
          _map(existCates, cate => {
            return this.cateRelationService.create({
              cid: cate.id,
              composeId: postId,
              composeType: SourceType.POST,
            });
          }),
        );
      }

      // -------- 文章标签表
      const tagsName = createPostDto.tags || [];
      if (tagsName.length) {
        // 检测tag是否存在
        const tags = await Promise.all(
          _map(tagsName, t => {
            return this.tagService.findByName(t);
          }),
        );
        // 需要新增的tag
        const newTags = [];
        const newTagIndex = [];
        const existTags: TagEntity[] = [...tags];
        _forEach(tags, (tag, index) => {
          if (_isUndefined(tag)) { // 为空即是不存在
            newTags.push(tagsName[index]);
            newTagIndex.push(index);
          }
        });
        // 新增加的tag
        const createTags = await Promise.all(
          _map(newTags, t => {
            return this.tagService.create({ name: t });
          }),
        );

        // 组合获取文章的标签详情数组
        _forEach(createTags, (value, index) => {
          const existIndex = newTagIndex[index];
          existTags[existIndex] = value;
        });

        // 批量插入标签文章映射
        // TODO 待优化
        await Promise.all(
          _map(existTags, et => {
            return this.tagRelationService.create({
              tagId: et.id,
              composeId: postId,
              composeType: SourceType.POST,
            });
          }),
        );
      }
      return post;
    });
  }
@silent-tan
Copy link
Author

实现是可以实现,但是觉得这种写法很挫。不用 @Transaction 修饰器是因为 typeorm0.4.0 版本好像是要删掉这些修饰器的

@silent-tan
Copy link
Author

silent-tan commented Jun 6, 2019

而且通过查看typeorm日志发现好像这种写法的话,没执行到最后就已经 commit

query: START TRANSACTION
query: INSERT INTO `post`(`id`, `created_at`, `update_at`, `deleted_at`, `type`, `title`, `sub_title`, `description`, `uid`, `content`, `status`, `cover`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, ?, ?, ?, ?, ?, ?, ?, ?) -- PARAMETERS: [0,"搭建 git 服务器","","",11,"1. 安装git:\n\n```cmd\n$ sudo apt-get install git\n```\n\n> [CentOS安装最新版git](https://my.oschina.net/antsky/blog/514586)\n\n2. 创建一个git用户,用来运行git服务:\n\n```cmd\n$ sudo adduser git\n```\n3. 创建证书登录:\n\n  收集所有需要登录的用户的公钥,就是他们自己的id_rsa.pub文件,把所有公钥导入到/home/git/.ssh/authorized_keys文件里,一行一个。\n\n4. 初始化Git仓库:\n\n  先选定一个目录作为Git仓库,假定是/srv/sample.git,在/srv目录下输入命令:\n\n```cmd\n$ sudo git init --bare sample.git\n```\n  Git就会创建一个裸仓库,裸仓库没有工作区,因为服务器上的Git仓库纯粹是为了共享,所以不让用户直接登录到服务器上去改工作区,并且服务器上的Git仓库通常都以.git结尾。然后,把owner改为git:\n\n```cmd\n$ sudo chown -R git:git sample.git\n```\n5. 禁用shell登录:\n\n  出于安全考虑,第二步创建的git用户不允许登录shell,这可以通过编辑/etc/passwd文件完成。找到类似下面的一行:\n\n```\ngit:x:1001:1001:,,,:/home/git:/bin/bash\n```\n\n  改为:\n```\ngit:x:1001:1001:,,,:/home/git:/usr/bin/git-shell\n```\n\n  这样,git用户可以正常通过ssh使用git,但无法登录shell,因为我们为git用户指定的git-shell每次一登录就自动退出。\n\n6. 克隆远程仓库:\n\n  现在,可以通过git clone命令克隆远程仓库了,在各自的电脑上运行:\n\n```cmd\n$ git clone git@server:/srv/sample.git\nCloning into 'sample'...\nwarning: You appear to have cloned an empty repository.\n```\n\n### 管理公钥\n如果团队很小,把每个人的公钥收集起来放到服务器的/home/git/.ssh/authorized_keys文件里就是可行的。\n\n### 来源\n- [廖雪峰的官方网站](http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/00137583770360579bc4b458f044ce7afed3df579123eca000)\n",1,"https://images.unsplash.com/photo-1547981607-6ea66fe762b4?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=900&h=600&fit=crop&ixid=eyJhcHBfaWQiOjF9"]
query: SELECT `PostEntity`.`id` AS `PostEntity_id`, `PostEntity`.`created_at` AS `PostEntity_created_at`, `PostEntity`.`update_at` AS `PostEntity_update_at`, `PostEntity`.`type` AS `PostEntity_type` FROM `post` `PostEntity` WHERE `PostEntity`.`id` = ? -- PARAMETERS: [18]
query: SELECT `CateEntity`.`id` AS `CateEntity_id`, `CateEntity`.`created_at` AS `CateEntity_created_at`, `CateEntity`.`update_at` AS `CateEntity_update_at`, `CateEntity`.`deleted_at` AS `CateEntity_deleted_at`, `CateEntity`.`name` AS `CateEntity_name`, `CateEntity`.`pid` AS `CateEntity_pid`, `CateEntity`.`cover` AS `CateEntity_cover`, `CateEntity`.`description` AS `CateEntity_description` FROM `cate` `CateEntity` WHERE `CateEntity`.`id` IN (?) LIMIT 1 -- PARAMETERS: [2]
query: SELECT `CateEntity`.`id` AS `CateEntity_id`, `CateEntity`.`created_at` AS `CateEntity_created_at`, `CateEntity`.`update_at` AS `CateEntity_update_at`, `CateEntity`.`deleted_at` AS `CateEntity_deleted_at`, `CateEntity`.`name` AS `CateEntity_name`, `CateEntity`.`pid` AS `CateEntity_pid`, `CateEntity`.`cover` AS `CateEntity_cover`, `CateEntity`.`description` AS `CateEntity_description` FROM `cate` `CateEntity` WHERE `CateEntity`.`id` IN (?) LIMIT 1 -- PARAMETERS: [3]
query: START TRANSACTION
query: START TRANSACTION
query: INSERT INTO `cate_relation`(`id`, `created_at`, `update_at`, `deleted_at`, `cid`, `compose_id`, `compose_type`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, ?, ?, ?) -- PARAMETERS: [2,18,1]
query: INSERT INTO `cate_relation`(`id`, `created_at`, `update_at`, `deleted_at`, `cid`, `compose_id`, `compose_type`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, ?, ?, ?) -- PARAMETERS: [3,18,1]
query: SELECT `CateRelationEntity`.`id` AS `CateRelationEntity_id`, `CateRelationEntity`.`created_at` AS `CateRelationEntity_created_at`, `CateRelationEntity`.`update_at` AS `CateRelationEntity_update_at` FROM `cate_relation` `CateRelationEntity` WHERE `CateRelationEntity`.`id` = ? -- PARAMETERS: [20]
query: SELECT `CateRelationEntity`.`id` AS `CateRelationEntity_id`, `CateRelationEntity`.`created_at` AS `CateRelationEntity_created_at`, `CateRelationEntity`.`update_at` AS `CateRelationEntity_update_at` FROM `cate_relation` `CateRelationEntity` WHERE `CateRelationEntity`.`id` = ? -- PARAMETERS: [21]
query: COMMIT
query: COMMIT
query: SELECT `TagEntity`.`id` AS `TagEntity_id`, `TagEntity`.`created_at` AS `TagEntity_created_at`, `TagEntity`.`update_at` AS `TagEntity_update_at`, `TagEntity`.`deleted_at` AS `TagEntity_deleted_at`, `TagEntity`.`name` AS `TagEntity_name`, `TagEntity`.`pid` AS `TagEntity_pid`, `TagEntity`.`color` AS `TagEntity_color`, `TagEntity`.`description` AS `TagEntity_description` FROM `tag` `TagEntity` WHERE `TagEntity`.`name` = ? LIMIT 1 -- PARAMETERS: ["git"]
query: START TRANSACTION
query: INSERT INTO `tag_relation`(`id`, `created_at`, `update_at`, `deleted_at`, `tag_id`, `compose_id`, `compose_type`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, ?, ?, ?) -- PARAMETERS: [7,18,1]
query: SELECT `TagRelationEntity`.`id` AS `TagRelationEntity_id`, `TagRelationEntity`.`created_at` AS `TagRelationEntity_created_at`, `TagRelationEntity`.`update_at` AS `TagRelationEntity_update_at` FROM `tag_relation` `TagRelationEntity` WHERE `TagRelationEntity`.`id` = ? -- PARAMETERS: [5]
query: COMMIT
query: COMMIT

是不是我哪个地方的理解有问题?

@silent-tan
Copy link
Author

哦,我可能知道为什么提前commit了,是不是只能通过调用manager方法去进行事务的原因....

@silent-tan
Copy link
Author

更改后就正常了

query: START TRANSACTION
query: INSERT INTO `post`(`id`, `created_at`, `update_at`, `deleted_at`, `type`, `title`, `sub_title`, `description`, `uid`, `content`, `status`, `cover`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, ?, ?, ?, ?, ?, ?, ?, ?) -- PARAMETERS: [0,"搭建 git 服务器2","","",11,"1. 安装git:\n\n```cmd\n$ sudo apt-get install git\n```\n\n> [CentOS安装最新版git](https://my.oschina.net/antsky/blog/514586)\n\n2. 创建一个git用户,用来运行git服务:\n\n```cmd\n$ sudo adduser git\n```\n3. 创建证书登录:\n\n  收集所有需要登录的用户的公钥,就是他们自己的id_rsa.pub文件,把所有公钥导入到/home/git/.ssh/authorized_keys文件里,一行一个。\n\n4. 初始化Git仓库:\n\n  先选定一个目录作为Git仓库,假定是/srv/sample.git,在/srv目录下输入命令:\n\n```cmd\n$ sudo git init --bare sample.git\n```\n  Git就会创建一个裸仓库,裸仓库没有工作区,因为服务器上的Git仓库纯粹是为了共享,所以不让用户直接登录到服务器上去改工作区,并且服务器上的Git仓库通常都以.git结尾。然后,把owner改为git:\n\n```cmd\n$ sudo chown -R git:git sample.git\n```\n5. 禁用shell登录:\n\n  出于安全考虑,第二步创建的git用户不允许登录shell,这可以通过编辑/etc/passwd文件完成。找到类似下面的一行:\n\n```\ngit:x:1001:1001:,,,:/home/git:/bin/bash\n```\n\n  改为:\n```\ngit:x:1001:1001:,,,:/home/git:/usr/bin/git-shell\n```\n\n  这样,git用户可以正常通过ssh使用git,但无法登录shell,因为我们为git用户指定的git-shell每次一登录就自动退出。\n\n6. 克隆远程仓库:\n\n  现在,可以通过git clone命令克隆远程仓库了,在各自的电脑上运行:\n\n```cmd\n$ git clone git@server:/srv/sample.git\nCloning into 'sample'...\nwarning: You appear to have cloned an empty repository.\n```\n\n### 管理公钥\n如果团队很小,把每个人的公钥收集起来放到服务器的/home/git/.ssh/authorized_keys文件里就是可行的。\n\n### 来源\n- [廖雪峰的官方网站](http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/00137583770360579bc4b458f044ce7afed3df579123eca000)\n",1,"https://images.unsplash.com/photo-1547981607-6ea66fe762b4?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=900&h=600&fit=crop&ixid=eyJhcHBfaWQiOjF9"]
query: SELECT `PostEntity`.`id` AS `PostEntity_id`, `PostEntity`.`created_at` AS `PostEntity_created_at`, `PostEntity`.`update_at` AS `PostEntity_update_at`, `PostEntity`.`type` AS `PostEntity_type` FROM `post` `PostEntity` WHERE `PostEntity`.`id` = ? -- PARAMETERS: [19]
query: SELECT `CateEntity`.`id` AS `CateEntity_id`, `CateEntity`.`created_at` AS `CateEntity_created_at`, `CateEntity`.`update_at` AS `CateEntity_update_at`, `CateEntity`.`deleted_at` AS `CateEntity_deleted_at`, `CateEntity`.`name` AS `CateEntity_name`, `CateEntity`.`pid` AS `CateEntity_pid`, `CateEntity`.`cover` AS `CateEntity_cover`, `CateEntity`.`description` AS `CateEntity_description` FROM `cate` `CateEntity` WHERE `CateEntity`.`id` IN (?) LIMIT 1 -- PARAMETERS: [2]
query: SELECT `CateEntity`.`id` AS `CateEntity_id`, `CateEntity`.`created_at` AS `CateEntity_created_at`, `CateEntity`.`update_at` AS `CateEntity_update_at`, `CateEntity`.`deleted_at` AS `CateEntity_deleted_at`, `CateEntity`.`name` AS `CateEntity_name`, `CateEntity`.`pid` AS `CateEntity_pid`, `CateEntity`.`cover` AS `CateEntity_cover`, `CateEntity`.`description` AS `CateEntity_description` FROM `cate` `CateEntity` WHERE `CateEntity`.`id` IN (?) LIMIT 1 -- PARAMETERS: [3]
query: INSERT INTO `cate_relation`(`id`, `created_at`, `update_at`, `deleted_at`, `cid`, `compose_id`, `compose_type`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, ?, ?, ?) -- PARAMETERS: [2,19,1]
query: INSERT INTO `cate_relation`(`id`, `created_at`, `update_at`, `deleted_at`, `cid`, `compose_id`, `compose_type`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, ?, ?, ?) -- PARAMETERS: [3,19,1]
query: SELECT `CateRelationEntity`.`id` AS `CateRelationEntity_id`, `CateRelationEntity`.`created_at` AS `CateRelationEntity_created_at`, `CateRelationEntity`.`update_at` AS `CateRelationEntity_update_at` FROM `cate_relation` `CateRelationEntity` WHERE `CateRelationEntity`.`id` = ? -- PARAMETERS: [22]
query: SELECT `CateRelationEntity`.`id` AS `CateRelationEntity_id`, `CateRelationEntity`.`created_at` AS `CateRelationEntity_created_at`, `CateRelationEntity`.`update_at` AS `CateRelationEntity_update_at` FROM `cate_relation` `CateRelationEntity` WHERE `CateRelationEntity`.`id` = ? -- PARAMETERS: [23]
query: SELECT `TagEntity`.`id` AS `TagEntity_id`, `TagEntity`.`created_at` AS `TagEntity_created_at`, `TagEntity`.`update_at` AS `TagEntity_update_at`, `TagEntity`.`deleted_at` AS `TagEntity_deleted_at`, `TagEntity`.`name` AS `TagEntity_name`, `TagEntity`.`pid` AS `TagEntity_pid`, `TagEntity`.`color` AS `TagEntity_color`, `TagEntity`.`description` AS `TagEntity_description` FROM `tag` `TagEntity` WHERE `TagEntity`.`name` = ? LIMIT 1 -- PARAMETERS: ["git"]
query: INSERT INTO `tag_relation`(`id`, `created_at`, `update_at`, `deleted_at`, `tag_id`, `compose_id`, `compose_type`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, ?, ?, ?) -- PARAMETERS: [7,19,1]
query: SELECT `TagRelationEntity`.`id` AS `TagRelationEntity_id`, `TagRelationEntity`.`created_at` AS `TagRelationEntity_created_at`, `TagRelationEntity`.`update_at` AS `TagRelationEntity_update_at` FROM `tag_relation` `TagRelationEntity` WHERE `TagRelationEntity`.`id` = ? -- PARAMETERS: [6]
query: COMMIT

@silent-tan
Copy link
Author

更改后的函数:

async create(createPostDto: CreatePostDto): Promise<PostEntity> {
    return await getManager().transaction(async manager => {
      const defaultCover = `https://images.unsplash.com/
      photo-1546623235-23f145a669ef?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=900&h=600&fit=crop&ixid=eyJhcHBfaWQiOjF9`;
      const postDto: PostDto = {
        title: createPostDto.title,
        uid: createPostDto.uid,
        content: createPostDto.content,
        type: createPostDto.type,
        status: createPostDto.status,
        cover: createPostDto.cover || defaultCover,
        subTitle: createPostDto.subTitle || '',
        description: createPostDto.description || '',
      };
      // 保存文章
      const post = await manager.save<PostEntity>(
        manager.create<PostEntity>(PostEntity, postDto),
      );
      const postId = post.id;

      // ------ 文章分类表
      const cids = createPostDto.cids || [];
      if (cids.length === 0) {
        // 默认未分类
        await manager.save<CateRelationEntity>(
          manager.create<CateRelationEntity>(
            CateRelationEntity,
            {
              cid: 14, // 未分类,
              composeId: postId,
              composeType: SourceType.POST,
            },
          ),
        );
      } else {
        const cates = await Promise.all(
          _map(cids, cid => {
            return this.cateService.findOne(cid);
          }),
        );
        // 过滤不存在的分类
        const existCates = _filter(cates, item => !_isUndefined(item));
        // 批量插入分类文章映射
        // TODO 待优化
        const cateRaltions = await Promise.all(
          _map(existCates, cate => {
            return manager.save<CateRelationEntity>(
              manager.create<CateRelationEntity>(CateRelationEntity, {
                cid: cate.id,
                composeId: postId,
                composeType: SourceType.POST,
              }),
            );
          }),
        );
      }

      // -------- 文章标签表
      const tagsName = createPostDto.tags || [];
      if (tagsName.length) {
        // 检测tag是否存在
        const tags = await Promise.all(
          _map(tagsName, t => {
            return this.tagService.findByName(t);
          }),
        );
        // 需要新增的tag
        const newTags = [];
        const newTagIndex = [];
        const existTags: TagEntity[] = [...tags];
        _forEach(tags, (tag, index) => {
          if (_isUndefined(tag)) { // 为空即是不存在
            newTags.push(tagsName[index]);
            newTagIndex.push(index);
          }
        });
        // 新增加的tag
        const createTags = await Promise.all(
          _map(newTags, t => {
            return manager.save<TagEntity>(
              manager.create<TagEntity>(TagEntity, {
                name: t,
              }),
            );
          }),
        );

        // 组合获取文章的标签详情数组
        _forEach(createTags, (value, index) => {
          const existIndex = newTagIndex[index];
          existTags[existIndex] = value;
        });

        // 批量插入标签文章映射
        // TODO 待优化
        await Promise.all(
          _map(existTags, et => {
            return manager.save<TagRelationEntity>(
              manager.create<TagRelationEntity>(TagRelationEntity, {
                tagId: et.id,
                composeId: postId,
                composeType: SourceType.POST,
              }),
            );
          }),
        );
      }
      return post;
    });
}

@dzzzzzy
Copy link
Owner

dzzzzzy commented Jun 6, 2019

getManager().transaction() 此方法会自动回滚

要自己控制事物可以用如下方式:

// 获取连接生成 query runner
const queryRunner = getConnection().createQueryRunner();
// 开启事务
await queryRunner.startTransaction();
try {
    const user = await queryRunner.manager.findOne(User, { id: userId });
    user.nickname = 'new_nickname';

    const article = await queryRunner.manager.findOne(Article, { id: articleId });
    article.title = 'new_title';

    // 保存 user 数据
    await queryRunner.manager.save(user);

    // 保存 article 数据
    await queryRunner.manager.save(article);

    // 提交事物
    await queryRunner.commitTransaction();
} catch (error) {
    // 回滚
    await queryRunner.rollbackTransaction();
} finally {
    // 释放连接
    await queryRunner.release();
}

@silent-tan
Copy link
Author

OK

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants