Typeorm中文文档

1,830 阅读7分钟

ActiveRecord 实体类的名字直接代表数据库中的表名,实体类里的字段代表数据库的字段

DataMapper 则不代表数据库具体含义,需要进行其他转换操作

安装和生成项目

npm install typeorm -g
typeorm init --name typeorm-demo --database mysql

配置文件(typeormconfig.json)

{
  "type": "mysql",      // 选用的数据库
  "host": "localhost",  // 数据库地址
  "port": 3306,         // 数据库端口
  "username": "test",   // 数据库用户名
  "password": "test",   // 数据库密码
  "database": "test",   // 数据库
  "synchronize": true,  // 是否同步(将entity下的模型自动同步到数据库中,若表已存在则报错)
  "dropSchema": true,   // 删除数据库中的表
  "logging": false,     // 是否打印日志(执行sql语句时输出原生sql,可配置["query", "error", "schema"]数组形式并指定sql的执行类型)
  "charset": "utf8mb4", // 数据库编码
  "timezone": "local",  // 时区,默认本地
  "entityPrefix": "",   // 数据表的前缀
  "entities": [         // typeorm查找模型的路径
     "src/entity/**/*.ts"
  ],
  "migrations": [       // 数据迁移文件生成的地方
     "src/migration/**/*.ts"
  ],
  "subscribers": [      // 订阅
     "src/subscriber/**/*.ts"
  ],
  "cli": {             // 数据迁移工具
     "entitiesDir": "src/entity",
     "migrationsDir": "src/migration",
     "subscribersDir": "src/subscriber"
  }
}

实体的方式(ActiveRecord)

import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn , Generated} from "typeorm";

@Entity({ 
  name: 'user', //数据库表名(若不指定,则以实体类名创建)
  orderBy:'',   //排序方式(一般不用)
  engine:'',    //引擎(一般不用)
  database:''   //数据库(一般不用,除非多个)
})
export class User {
  @PrimaryColumn('int',{generated:true}) //等同PrimaryGeneratedColumn
  othId:number
  
  @PrimaryGeneratedColumn({ //自增主键
    type: 'int',            
    name: 'id',          
    comment: '主键id'
  })
  id: number;

  @Column({ //普通字段
    type: 'varchar',    //字段类型(字符型:char、varchar、text)(数字型:int、tinyint)(小数型:float、double、decimal(10,2))等
    nullable: false,    //是否可为null(默认false)
    length: 50,         //当字段为字符型时,可指定长度
    unique: true,       //是否唯一约束
    name: 'username',   //数据库字段名字(若不指定,则为实体的属性,即此处的 username:string)
    comment: '用户名'    //字段注释
  })
  username: string;

  @Column({
    type: 'varchar',
    nullable: false,
    length: 100,
    comment: '密码'
  })
  password: string;

  @Column('tinyint', {  //Column是可重载的方法
    nullable: false,
    default: () => 0,
    name: 'is_del',
    comment: '是否删除,1表示删除,0表示正常'
  })
  isDel: number;

  @CreateDateColumn({  //自动生成列
    type: 'timestamp',
    nullable: false,
    name: 'created_at',
    comment: '创建时间'
  })
  createdAt: Date;

  @UpdateDateColumn({ //自动生成并自动更新列
    type: 'timestamp',
    nullable: false,
    name: 'updated_at',
    comment: '更新时间',
  })
  updateAt: Date;
  
  @Column({ 
    comment: '性别', 
    type: 'enum',     //枚举
    enum: SexRole,    
    default: SexRole.BOY 
  })  
  sex: SexRole
  
  @Column("simple-array")  // 数组
  names: string[];

  @Column("simple-json")   // 对象
  profile: { name: string; nickname: string };
  
  @Column({unique:true})  // 唯一
  @Generated("uuid")      // 自动生成列(生成策略uuid)
  token: string;
}

EntityManager(实体管理器)和EntityRepository(实体仓储)

import "reflect-metadata";
import { createConnection } from "typeorm";
import { User } from "./entity/User";

createConnection().then(async connection => {
  const user = new User()
  user.username = 'Little brother';
  user.password = '123456';
  
  //1.实体-管理器回调方式
  connection.manager.save(user).then(user => {
    console.log('实体-管理器回调:', user);
  });
  let allUserByManage = await connection.manager.find(User);
  console.log('实体-全部用户:',allUserByManage)
  
  //2.实体-管理器async/await方式
  const result = await connection.manager.save(user);
  console.log('实体-管理器同步:', result);
  
  
  //3.实体-仓储方式
  const userRepository = connection.getRepository(User);
  const result = await userRepository.save(user);
  console.log('实体-仓储方式:',result);
  
  let allUserByRep = await userRepository.find()
  console.log('实体-仓储全部用户:',allUserByRep);
  
}).catch(error => console.log(error));

EntityManager(实体管理器)和EntityRepository(实体仓储)的区别

类别不同
EntityManager可以直接操作实体对象,new出来的对象
EntityRepository需要先获取 实体类的引用,在增删查改实体对象
两者共有方法作用
update更新
findOnefindOne(1) 只能通过idObjectId 等查找
save保存
delete删除
clear清空
createQueryBuilder查询器
query直接执行sql

表关系(一对一)

import {Entity, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn,createConnection} from "typeorm";

@Entity()
export class Photo {  //图片的实体类

    @PrimaryGeneratedColumn()
    id: number;
    
    @Column()
    url:string

    @OneToOne(type => PhotoMetadata, photoMetadata => photoMetadata.photo)
    metadata: PhotoMetadata;
}

@Entity()
export class PhotoMetadata {  //图片元数据实体类
	
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    orientation: string;

    @Column()
    compressed: boolean;

    @OneToOne(type => Photo,photo=>photo.metadata)  //对应关系是Photo实体类,如果是单项关系,oneToOne第二哥参数可不传
    @JoinColumn()     //表明实体键的对应关系,与Photo关联(元数据是图片的一种属性,所以可理解PhotoMeatadata插入到Photo的一个属性中,所以是JointColumn:插入列)
    photo: Photo;
}

//index.ts
createConnection().then(async ()=>{
   //1.获取photo和photoMetadata的实体
   const photoRepo=connection.getRepository(Photo)
   const photoMetaRepo=connection.getRepository(PhotoMetadata)
   
   //2.创建photo和photoMetadata实体对象
   const photo=new Photo();
   photo.url="www.baidu.com"

   const photoMeta=new PhotoMetadata()
   photoMeta.orientation="right"
   photoMeta.compressed=false
   
   //3.关联photoMetadata和photo
   photoMeta.photo=photo
   
   //4.先保存photo再保存photoMetadata(photoMeta是保存在photo中的,先有photo,不然photoMetadata没办法保存)
   await photoRepo.save(photo) 
   await photoMetaRepo.save(photoMeta) 
   
   //5.查找photo列表(正向查找)
   const photoList=photoRepo.find({relations:['metadata']})
   
   //6.查找photoMeta列表(反向查找)
   const photoMetaList=photoMetaRepo.find({relations:['photo]})
})

表关系(一对多)

import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm";

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

  @Column()
  name: string;
  
  //user下有多个photo,使用OneToMany
  //user表中不会新增photos字段
  //数据库没有array类型,但是typeorm执行sql时,会返回该photos字段的集合
  //cascade 自动保存关系对象,就不用user和photo依依保存,直接保存user就好
  //cascade可选参数有 {cascadeInsert:true,cascadeUpdate:true,cascadeRemove:true}
  @OneToMany(type => Photo, photo => photo.user,{cascade:true})
  photos: Photo[];
}

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

  @Column()
  url: string;

  //一个Photo在user的一个字段的集合中用ManyToOnE
  //photo表中新增userId 指向user表中的id字段
  //ManyToOne会自动生成数据库列,相当隐式加了@Column注解
  @ManyToOne(type => User, user => user.photos,{cascade:true})
  user: User;
}


createConnection.then(async (connection)=>{
  //1.创建user和photo仓储对象
  const userRepo=connection.getRepository()
  const photoRepo=connection.getRepository()
  
  //2.查找user
  const userList = await userRepo.find({ relations: ["photos"] });

  //3.查找photo
  const photoList = await photoRepo.find({ relations: ["user"] });
  
})

表关系(多对多):此情况一般情况依赖一张中间表存放两种表的主键

多张图片,多个相册.每张图片可能会存在每个相册中

import {Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable} from "typeorm";

@Entity()
export class Album { //相册实体

  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @ManyToMany(type => Photo, photo => photo.albums)
  @JoinTable({name:'alum_photo'})   //多对多关系必须指定关系拥有者,JoinTable插入中间表(name可指定表名,默认则以此处拥有者Alum,被拥有者Photo关系创建中间表 alum_photos_photo)
  photos: Photo[];
}

@Entity()
export class Photo { //图片实体
  @PrimaryGeneratedColumn()
  id:number
  
  @Column()
  url:string

  @ManyToMany(type => Album, album => album.photos) //一张图片在多个相册中,ManyToMany
  albums: Album[];
}

createConnection().then(async connection => {
  //先清空下数据库表,因为都是findOne(1),id=1
  // 1.创建两个相册
  let albumOne = new Album();
  albumOne.name = "鸭子的相册";
  await connection.manager.save(albumOne);

  let albumSecond = new Album();
  albumSecond.name = "小鸡的相册";
  await connection.manager.save(albumSecond);

  // 2.创建一张图片 
  let photo = new Photo();
  photo.url = "www.baidu.com";
  photo.albums = [albumOne, albumSecond];
  await connection.manager.save(photo);

  const findPhoto = await connection.getRepository(Photo).findOne(1, { relations: ["albums"] });

}).catch(error => console.log(error));

EntityManager实体管理器和EntityRepository实体仓库上的查询方法

方法含义
find查找返回一个数组
findOne查询一条数据,默认传数字,根据id查询,可传option查询参数对象
findAndCount查询列表并返回数量 [data,count]
findByIds([])根据[id1,id2] ID数组查询
以下是例子

const userRepo=connection.getRepository(User) | 例子 | 解释 | sql语句 | |------|------------|------------| | userRepo.find() | 查询所有 | select * from user | | userRepo.find({select:['id','name']}) | 根据字段查询 | select id,username from user | | userRepo.find({where:{id:1}}) | 查询id=1的数据 | select * from user where user.id=1 | | userRepo.find({where:{id:1,name:'little duck'}}) | AND条件查询 | select * from user where user.id=1 and user.name='little duck' | | userRepo.find({where:[{id:1},{name:'little duck'}]}) | OR条件查询 | select * from user where user.id=1 or user.name='little duck' | | userRepo.find({relations:['userInfo']}) | 关联查询 | | | userRepo.find({join:{alias:'user',leftJointAndSelect:{detail:'user.userDetail',name:'user.name'}}}) | join查询 | | | userRepo.find({order:{id:'desc',name:'asc'}}) | order查询,id降序,name升序 | select * from user | | userRepo.find({skip:0,take:10}) | 分页 skip偏移(从此处开始查) take(查询多少条) | |

EntityManage 和 EntityGetRepository QueryBuilder 查询器

connection.manager 和connnecttion.getRepository对象上都有createQueryBuilder方法

也可使用getConnection和getManager方法创建

getConnection()创建一:
const user = await getConnection().createQueryBuilder(User, 'user')
                                  .select(['user.id', 'user.username'])
                                  .where('(user.id=:id)', { id: 1 })
                                  .getOne();
  
getConnection()创建二:
const user = await getConnection().createQueryBuilder()
                                  .select(['user.id', 'user.username'])
                                  .from(User, 'user') 
                                  .where('(user.id=:id)', { id: 1 }) 
                                  .getOne(); 
  
getManager()创建:
const user = await getManager().createQueryBuilder(User, 'user')
                               .select('user')
                               .getMany();

manager()创建:
const user= await connection.manager.createQueryBuilder('user').getMany()

getRepository()创建:
const user = await getRepository(User).createQueryBuilder('user')
                                      .getMany();
                                      
 

QueryBuilder增删改查示例

//1.查询
const user = await getConnection()
  .createQueryBuilder(User, 'user')
  .select(['user.id', 'user.username'])
  .where('(user.id=:id)', { id: 1 })
  .getOne();

//2.增加
const result = await getConnection()
  .createQueryBuilder()
  .insert() // 插入数据的时候要指明插入到那个实体类
  .into(User)
  .values([{ username: '张三', password: '1234' }, { username: '李四', password: '12345' }])
  .execute();     

//更新
const result = await getConnection()
  .createQueryBuilder()
  .update(User)
  .set({ username: '哈哈哈' })
  .where('id=:id', { id: 1 })
  .execute();
  
//删除
const result = await getConnection()
  .createQueryBuilder()
  .delete()
  .from(User)
  .where('id=:id', { id: 3 })
  .execute();
  
//关系查询
const result = await getConnection()
  .createQueryBuilder(User, 'user')
  // 第一个参数是定义字段,第二个实体类,第三个是别名,第四个是条件
  .leftJoinAndMapMany('user.posts', Posts, 'posts', 'user.id=posts.userId')
  .getMany();
  
//聚合查询
const result = await getConnection()
  .createQueryBuilder(User, 'user')
  // 使用了聚合函数就要使用getRawOne或者getRawMany方法
  .select('SUM(user.id)', 'sum')
  .getRawOne();

参数

等级掘力值下限
where传参.where("user.username = :username", { username: "哈哈" })
setParameter传参.where("user.username = :username").setParameter("username", "哈哈")
link模糊查询.where("user.username like :username", {username: % %{username} %})
IN查询.where("user.username IN (:...username)", { username: [ "Timber", "Cristal", "Lina" ] })

migration迁移

配置脚本命令:
"generate": "ts-node  ./node_modules/typeorm/cli.js migration:generate -n Test",
"db": "ts-node ./node_modules/typeorm/cli.js migration:run"

修改前运行:
npm run generate
npm run db