typeorm连表查没有关联关系的实体

简介: typeorm连表查没有关联关系的实体

深入探究 TypeORM:前端开发者的后端数据操作利器

在现代的软件开发中,前后端分离已经成为了一种趋势。作为前端开发者,我们有时也需要涉足后端领域,特别是在处理数据持久化时。当我们谈论数据持久化,数据库无疑是其中的核心组件。但对于前端开发者来说,直接操作数据库可能会显得有些陌生和复杂。这时,ORM(对象关系映射)工具就派上了用场。在Node.js生态系统中,TypeORM是一个非常受欢迎的ORM选择。

什么是 TypeORM?

TypeORM 是一个在 Node.js、Browser、Cordova、PhoneGap、Ionic、React Native、NativeScript、Expo 和 Electron 平台上支持 TypeScript 和 JavaScript(ES5, ES6, ES7, ES8)的 ORM。它可以在 NodeJS、浏览器、Cordova、PhoneGap 和 Ionic 中运行,支持 MySQL / MariaDB / Postgres / SQLite / Microsoft SQL Server / Oracle / sql.js 等多种数据库,并提供了强大的模型定义、数据操作等功能。

为何选择 TypeORM?

  1. 类型安全:由于 TypeORM 支持 TypeScript,因此它提供了类型安全的数据操作。这意味着在编译时就能捕获许多常见的错误。
  2. 易于上手:对于前端开发者来说,TypeORM 的 API 设计直观且友好,很多概念与前端框架中的模式相似。
  3. 活跃的社区:TypeORM 有一个庞大的用户基础和活跃的社区,这意味着遇到问题时可以迅速找到帮助。
  1. 支持多种数据库:无论是关系型数据库还是非关系型数据库,TypeORM 都提供了良好的支持。

开始使用 TypeORM

在使用 TypeORM 之前,需要先安装相应的依赖:

npm install typeorm

连接数据库

首先,需要创建一个数据库连接。这通常在一个名为 app.tsindex.ts 的文件中完成:

import { createConnection } from 'typeorm';

createConnection({
  type: 'mysql',
  host: 'localhost',
  port: 3306,
  username: 'test',
  password: 'test',
  database: 'test',
  entities: [__dirname + '/entity/*.js'],
  synchronize: true,
}).then(connection => {
  console.log('Connected to the database');
}).catch(error => {
  console.log('Error connecting to the database:', error);
});

在上面的代码中,createConnection 方法接受一个包含数据库连接信息的对象。entities 属性指定了实体类的位置,这些实体类将映射到数据库中的表。synchronize 属性设置为 true 时,TypeORM 将自动同步实体类和数据库表的结构。

定义实体

实体是 TypeORM 中的核心概念,它代表了数据库中的一个表。例如,我们可以定义一个 User 实体:

import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  email: string;
}

在上面的代码中,@Entity 装饰器标记了这个类是一个实体类。@PrimaryGeneratedColumn 装饰器表示 id 属性是主键,并且是自动生成的。@Column 装饰器标记了其他的列。

数据操作

有了实体类之后,就可以进行数据的增删改查了。以下是一些基本的操作示例:

import { getRepository } from 'typeorm';
import { User } from './entity/User';

// 查找用户
const userRepository = getRepository(User);
const user = await userRepository.findOne({ email: 'example@example.com' });

// 创建用户
const newUser = new User();
newUser.name = 'John Doe';
newUser.email = 'john@example.com';
await userRepository.save(newUser);

// 更新用户
user.name = 'Jane Doe';
await userRepository.save(user);

// 删除用户
await userRepository.delete(user.id);

在上面的代码中,getRepository 方法用于获取指定实体的存储库,这个存储库提供了对实体进行增删改查的方法。

进阶使用

当然,让我们更深入地探讨TypeORM的一些进阶功能,这些功能在构建复杂应用程序时特别有用。

关系映射

在关系型数据库中,数据通常分布在多个相关联的表中。TypeORM通过关系映射装饰器(如@OneToOne@OneToMany@ManyToOne@ManyToMany)允许你在实体类之间建立这些关系。这意味着你可以轻松地在TypeORM实体之间创建外键关系,并通过简单的API调用访问相关联的数据。

例如,如果你有一个User实体和一个Profile实体,每个用户都有一个个人资料,你可以这样设置关系:

@Entity()
export class User {
  // ...其他字段

  @OneToOne(type => Profile, profile => profile.user)
  profile: Profile;
}

@Entity()
export class Profile {
  // ...其他字段

  @OneToOne(type => User, user => user.profile)
  user: User;
}

使用关系映射后,你可以通过简单的API调用获取用户的个人资料,或者通过个人资料获取用户信息。

事务管理

事务是一系列必须作为单个工作单元执行的数据库操作。如果事务中的所有操作都成功,则事务被提交,并且更改永久保存在数据库中。如果事务中的任何操作失败,则整个事务都会回滚,数据库恢复到事务开始之前的状态。

TypeORM提供了强大的事务管理功能,允许你在多个操作之间保持数据一致性。你可以使用QueryRunner类或getConnection().transaction()方法来管理事务。

const queryRunner = connection.createQueryRunner();

await queryRunner.connect();
await queryRunner.startTransaction();

try {
  // 执行一些数据库操作
  // ...

  await queryRunner.commitTransaction();
} catch (error) {
  await queryRunner.rollbackTransaction();
} finally {
  await queryRunner.release();
}

或者使用更简洁的transaction方法:

await connection.transaction(async manager => {
  // 执行一些数据库操作,使用manager代替常规repository
  // ...
});

查询构建器

查询构建器是TypeORM中用于构建和执行复杂数据库查询的强大工具。它提供了链式调用语法,使得构建查询变得简单且直观。你可以使用查询构建器来创建各种类型的查询,包括选择、插入、更新和删除操作。

下面是一个使用查询构建器选择用户的例子:

const users = await connection.getRepository(User).createQueryBuilder("user")
  .where("user.name = :name", { name: "John" })
  .orWhere("user.email = :email", { email: "john@example.com" })
  .getMany();

查询构建器还支持更复杂的查询,如连接多个表、分组、排序以及使用原生SQL片段等。


通过这些进阶功能,TypeORM为开发者提供了一个全面且灵活的解决方案,以应对各种数据持久化挑战。无论是构建简单的CRUD应用还是复杂的业务逻辑,TypeORM都能提供所需的工具和抽象,使数据库操作变得更加容易和可维护。

注意:orm框架中,尽量不要加外键关联

弱弱的问一下

你们觉得typeorm好用嘛?为啥我觉得挺麻烦的啊?联查必须要给定一个关系,也就是说必须有外键,而且设置起来必须有manytoone和onetomany,而且储存的时候也必须按照他的一套来,而且manytoone的关联表竟然不返回主表的id,还要自己再指定一个比如说user_id的字段自己保存了,才能返回,他自己生成的userId并不返回。而且他的tree实体,看起来挺好的,但是用起来,真是难用,因为没法指定where条件啊。还是我不会用啊?就问问大家所有的orm框架都这么难搞嘛?我已经没写过sql很多年了。就拿一个查tree模型部门结构来说,我是自己写的先查根,再遍历根查孩子,这样子递归,可是这样子就有点拉跨,多查了很多次数据库,这样算法复杂度一下子上去了,部门少还没事。我就很郁闷,想知道大家都是怎么用orm框架的啊

连表查没有关联关系的实体

Dictionary_type实体

//定义Dictionary_type实体,字典类型
@EntityModel()
export class Dictionary_type {

  @PrimaryGeneratedColumn("uuid")
  id: string;

  @Column({comment: "编码",nullable: true,})
  code: string;

  @Column({comment: "状态", nullable: true,default:1})
  state: number;

  @Column({comment: "字典类型名称", nullable: true})
  text: string;
  

  @CreateDateColumn()
  creatDate: string;

  @UpdateDateColumn()
  updateDate: string;

}

Dictionary_item 实体

在实体中设置type_id存放Dictionary_type主键id

@EntityModel()
export class Dictionary_item {

  @PrimaryGeneratedColumn("uuid")
  id: string;

  @Column({comment: "序号",nullable: true,})
  sort: number;

  @Column({comment: "字典类型", nullable: true,})
  type_id: string;

  @Column({comment: "字典类型值", nullable: true})
  value: number;

  @Column({comment: "字典类型内容", nullable: true})
  text: string;


  @CreateDateColumn()
  creatDate: string;

  @UpdateDateColumn()
  updateDate: string;

}

Post和PostExtend中没有设置关联关系,所以我们并不能在find option中关联两个实体进行连表查询。

但是可以用queryBuilder

 const posts = await getConnection()
          .createQueryBuilder(Post, 'post')
          .leftJoinAndSelect(PostExtend, 'postExtend', 'post.id=postExtend.postId')
         .getManyAndCount()
return posts;

查询结果:

[
    [
        {
            "id": 1,
            "title": "北京申奥成功",
            "content": "2003年奥林匹克运动会将在北京举行,北京欢迎你!"
        }
    ],
    1
]

上面的查询结果中并没有PostExtend的数据,这是因为不能确定两个实体之间的关联关系,所以无法确定查询结果的显示形式。

当然,也可以通过 getRawMany() 方法获取原生字段来获取PostExtend的信息,但是这样的查询结果显示并不友好。

1 const posts = await getConnection()
2             .createQueryBuilder(Post, 'post')
3             .leftJoinAndSelect(PostExtend, 'postExtend', 'post.id=postExtend.postId')
4             .getRawMany()
5         return posts;

结果:


 1 [
 2     {
 3         "post_id": 1,
 4         "post_title": "北京申奥成功",
 5         "post_content": "2003年奥林匹克运动会将在北京举行,北京欢迎你!",
 6         "postExtend_id": 1,
 7         "postExtend_postId": 1,
 8         "postExtend_likeCount": 999,
 9         "postExtend_readCount": 10000,
10         "postExtend_forwardCount": 666
11     }
12 ]

如果想要将原生字段映射到属性,可以使用 leftJoinAndMapOne() ,如果时一对多还可以使用 leftJoinAndMapMany()

 const posts = await getConnection()
             .createQueryBuilder(Post, 'post')
             .leftJoinAndMapOne('post.postExtend',PostExtend, 'postExtend', 'post.id=postExtend.postId')
             .getManyAndCount()
         return posts;

结果:

[
 2     [
 3         {
 4             "id": 1,
 5             "title": "北京申奥成功",
 6             "content": "2003年奥林匹克运动会将在北京举行,北京欢迎你!",
 7             "postExtend": {
 8                 "id": 1,
 9                 "postId": 1,
10                 "likeCount": 999,
11                 "readCount": 10000,
12                 "forwardCount": 666
13             }
14         }
15     ],
16     1
17 ]

postExtend的数据被映射到post.postExtend,这样的结果清晰明了。

相关文章
|
18天前
|
JSON Java API
Java一分钟之-JPA实体关系:一对一, 一对多, 多对多
【6月更文挑战第14天】Java Persistence API (JPA) 的 ORM 规范简化了数据库操作,重点是实体关系映射。本文讨论了三种主要关系:一对一、一对多和多对多。对于每种关系,都指出了常见问题(如循环引用、懒加载异常)和避免策略(使用注解解决循环引用,明确级联操作边界等)。同时,提供了示例代码以展示如何在实践中设置这些关系。理解并妥善处理这些问题能提升开发效率和数据准确性。
20 1
|
10月前
|
关系型数据库 MySQL 数据库
SQLAlchemy关联表一对多关系的详解
SQLAlchemy关联表一对多关系的详解
|
9月前
|
SQL XML 数据格式
mybatis-关联关系&一对多关联&多对一关联&多对多
mybatis-关联关系&一对多关联&多对一关联&多对多
|
测试技术 数据库
Grails里DOMAIN类的一对一,一对多,多对多关系总结及集成测试
终于干完这一章节,收获很多啊。 和DJANGO有类似,也有不同。 User.groovy: package com.grailsinaction class User { String loginId String password Date dat...
1245 0
SpringDataJPA之关联关系
本文介绍下SpringDataJPA怎么处理关联关系