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 | 更新 | |
| findOne | findOne(1) 只能通过id | ObjectId 等查找 |
| 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