TypeORM初级
本章先不讲解在Nest框架里面使用TypeORM连接数据库
- 先讲解TypeORM的基本用法
- 初级部分讲解基本使用
- 高级部分讲解表联查,一对一,一对多,多对多
- 最后在Nest里实现一个小Deom
什么是ORM?
- ORM 是 Object Relational Mapping,对象关系映射。
- 关系型数据库的表映射成面向对象的 class,
- 表的字段映射成对象的属性映射,
- 表与表的关联映射成属性的关联。
初体验
创建连接
安装命令
npx typeorm@latest init --name typeorm-all-feature --database mysql
- 通过 typeorm 的 init 命令创建一个项目, --name 指定项目名,--database 指定连接的数据库。
- 我的本地数据库密码是 root root123
接下来修改data-source.ts这个文件
//data-source.ts
import "reflect-metadata"
import { DataSource } from "typeorm"
import { User } from "./entity/User"
export const AppDataSource = new DataSource({
type: "mysql", // 数据库类型
host: "localhost", // 数据库地址
port: 3306, // 数据库端口
username: "root", // 数据库用户名
password: "root123", // 数据库密码
database: "test", // 数据库名
synchronize: true, // 自动同步实体
logging: true, // 开启日志
entities: [User], // 实体
migrations: [], // 迁移
subscribers: [], // 订阅
connectorPackage: 'mysql2', // 数据库驱动
extra: { // 额外配置
authPlugin: 'sha256_password', // 密码加密方式
}
})
安装下 mysql2 这个驱动包
npm install --save mysql2
ok了配置好了 接下来启动一下
npm run start
完美
数据表哪里来的呢?
-
我们的目录entity下面有一个默认的User.ts文件
-
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm" @Entity() // 标明这是一个实体 export class User { @PrimaryGeneratedColumn() // 标明这是一个主键 id: number @Column() // 标明这是一个列 firstName: string @Column() lastName: string @Column() age: number } - 通过装饰器声明了主键列和其他的列
-
初始数据哪里来的呢?
-
index.ts文件
-
AppDataSource.initialize().then(async () => { const user = new User() // 创建实体 user.firstName = "Timber" // 赋值 user.lastName = "Saw" // 赋值 user.age = 25 // 赋值 await AppDataSource.manager.save(user) // 保存 const users = await AppDataSource.manager.find(User) // 查询 }).catch(error => console.log(error)) - 在 index.ts 里创建了一个 User 的对象,调用 save 方法保存这个对象,通过 find 方法查询这个对象:
-
创建表
我们创建一个表,也就是实体(Entity)试一下
比如我在创建一张商品表(good)
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"
@Entity({
name: "good" // 表名
})
export class Good {
@PrimaryGeneratedColumn({
type: "int", // 类型
name: "id", // 字段名
comment: "这是主键" // 备注
})
id: number;
@Column({
unique: true, //唯一
type: "varchar", //数据库类型
length: 20, //长度
comment: "商品名字"
})
name: string;
@Column({
name: "good_type", // 字段名
nullable: false, //不能是空
length: 10,
type: 'varchar',
default: 'bbb' //默认值
})
type: string
@Column({
name: "price", // 字段名
nullable: false, //不能是空
length: 10,
default: '100', //默认值
})
price: string
}
- 我们新增了一个 Entity Good
- @Entity 指定它是一个 Entity,name 指定表名为 good
- @PrimaryGeneratedColumn 指定它是一个自增的主键,通过 comment 指定注释
- @Column 映射属性和字段的对应关系
- 通过 name 指定字段名
- type 指定映射的类型
- length 指定长度
- default 指定默认值
- nullable 设置 NOT NULL 约束
- unique 设置 UNIQUE 唯一索引
- type 这里指定的都是数据库里的数据类型
接下来在 DataSource 的 entities 里引入下
import "reflect-metadata"
import { DataSource } from "typeorm"
import { User } from "./entity/User"
import { Good } from "./entity/Good" //1.导入
export const AppDataSource = new DataSource({
type: "mysql",
host: "localhost",
port: 3306,
username: "root",
password: "root123",
database: "test",
synchronize: true,
logging: true,
entities: [User, Good], //2.注入
migrations: [],
subscribers: [],
connectorPackage: 'mysql2',
extra: {
authPlugin: 'sha256_password',
}
})
OK了
npm run start
我们看一下数据库
很完美,这样就成功创建出表了
增删改查(CRUD)
save(增加|修改)
-
如果指定了id就是修改
-
指定了 id 的时候,typeorm 会先查询这个 id 的记录,如果查到了,那就执行 update
-
其实 EntityManager 还有 update 和 insert 方法,分别是修改和插入的,
- 区别:它们不会先 select 查询一次。而 save 方法会先查询一次数据库来确定是插入还是修改。
-
-
没有id当然就是添加了
//没有指定id
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"
AppDataSource.initialize().then(async () => {
const good = new Good()
good.name = "Apple"
good.type = "手机产品"
good.price = "10"
await AppDataSource.manager.save(good)
}).catch(error => console.log(error))
npm run start //执行一下
//指定id
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"
AppDataSource.initialize().then(async () => {
const good = new Good()
good.id = 1
good.name = "Apple 修改之后"
good.type = "手机产品"
good.price = "10000"
await AppDataSource.manager.save(good)
}).catch(error => console.log(error))
npm run start //执行一下
save(批量添加和修改)
- 和一个没什么区别
//没有id的 添加
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"
AppDataSource.initialize().then(async () => {
await AppDataSource.manager.save(Good, [
{ name: 'ccc', type: 'ccc', price: "21" },
{ name: 'ddd', type: 'ddd', price: "22" },
{ name: 'eee', type: 'eee', price: "23" }
])
}).catch(error => console.log(error))
//有id的 修改
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"
AppDataSource.initialize().then(async () => {
await AppDataSource.manager.save(Good, [
{ id: 2, name: 'ccc修改', type: 'ccc修改', price: "200001" },
{ id: 3, name: 'ddd修改', type: 'ddd', price: "200002" },
{ id: 4, name: 'eee', type: 'eee', price: "23" }
])
}).catch(error => console.log(error))
OK了
delete (删除)
单条和批量删除无非就是穿值和数组
- 单条删除
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"
AppDataSource.initialize().then(async () => {
await AppDataSource.manager.delete(Good, 1);
}).catch(error => console.log(error))
- 批量删除
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"
AppDataSource.initialize().then(async () => {
await AppDataSource.manager.delete(Good, [3, 4]);
}).catch(error => console.log(error))
大概就是这样 记得每次写完执行
npm run start //执行一下
remove (删除)
- 单条删除
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"
AppDataSource.initialize().then(async () => {
const good = new Good()
good.id = 2
await AppDataSource.manager.remove(Good, good)
}).catch(error => console.log(error))
两个删除的区别
delete 和 remove 的区别是,delete 直接传 id、而 remove 则是传入 entity 对象
查询
-
查询东西就比较多了
-
find 查询数据
-
findBy 根据条件查询
-
findAndCount 多少条记录
-
findOne 查询一条
-
findOneBy
- findOneOrFail
- findOneByOrFail
-
query 直接写sql语句
-
createQueryBuilder 写sql语句 简化版 不会sql不熟练也没关系
-
transaction:包裹一层事务的 sql
-
getRepository:拿到对单个 Entity 操作的类,方法同 EntityManager
-
欧克现在我们试试吧
先插入一些数据
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"
AppDataSource.initialize().then(async () => {
await AppDataSource.manager.save(Good, [
{ name: '小米', type: '手机', price: "1000" },
{ name: '华为', type: '手机', price: "5000" },
{ name: '红米', type: '手机', price: "2000" },
{ name: 'OPPO', type: '手机', price: "1500" },
{ name: '华硕', type: '电脑', price: "2200" },
{ name: '小米1', type: '电脑', price: "2000" },
{ name: '苹果1', type: '电脑', price: "99999" },
{ name: '小米2', type: '平板', price: "4999" },
{ name: '华为2', type: '平板', price: "399" },
])
}).catch(error => console.log(error))
find
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"
AppDataSource.initialize().then(async () => {
const Goods = await AppDataSource.manager.find(Good);
console.log(Goods);
}).catch(error => console.log(error))
看一下打印的数据
[
Good { id: 16, name: '小米', type: '手机', price: '1000' },
Good { id: 17, name: '华为', type: '手机', price: '5000' },
Good { id: 18, name: '红米', type: '手机', price: '2000' },
Good { id: 19, name: 'OPPO', type: '手机', price: '1500' },
Good { id: 20, name: '华硕', type: '电脑', price: '2200' },
Good { id: 21, name: '小米1', type: '电脑', price: '2000' },
Good { id: 22, name: '苹果1', type: '电脑', price: '99999' },
Good { id: 23, name: '小米2', type: '平板', price: '4999' },
Good { id: 24, name: '华为2', type: '平板', price: '399' }
]
可以看到,打印的是数组对象
findBy(条件查询)
//名字等于小米的
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"
AppDataSource.initialize().then(async () => {
const Goods = await AppDataSource.manager.findBy(Good, {
name: "小米"
});
console.log(Goods);
}).catch(error => console.log(error))
//打印的数据
[ Good { id: 16, name: '小米', type: '手机', price: '1000' } ]
findAndCount (多少条记录)
- 可以解构出来 第一个就是数据了 第二个 就是条数
//不仅可以获得数据,也可以获取到条数
AppDataSource.initialize().then(async () => {
const [goods, count] = await AppDataSource.manager.findAndCount(Good);
console.log(goods, count);
}).catch(error => console.log(error))
//打印的数据
[
Good { id: 16, name: '小米', type: '手机', price: '1000' },
Good { id: 17, name: '华为', type: '手机', price: '5000' },
Good { id: 18, name: '红米', type: '手机', price: '2000' },
Good { id: 19, name: 'OPPO', type: '手机', price: '1500' },
Good { id: 20, name: '华硕', type: '电脑', price: '2200' },
Good { id: 21, name: '小米1', type: '电脑', price: '2000' },
Good { id: 22, name: '苹果1', type: '电脑', price: '99999' },
Good { id: 23, name: '小米2', type: '平板', price: '4999' },
Good { id: 24, name: '华为2', type: '平板', price: '399' }
] 9
findAndCountBy(多少条记录,可筛选)
- 可以指定条件
- 会返回筛选之后的数据和条数
//数据价格是2000的
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"
AppDataSource.initialize().then(async () => {
const [goods, count] = await AppDataSource.manager.findAndCountBy(Good, {
price: "2000"
});
console.log(goods, count);
}).catch(error => console.log(error))
//打印的数据
[
Good { id: 18, name: '红米', type: '手机', price: '2000' },
Good { id: 21, name: '小米1', type: '电脑', price: '2000' }
] 2
findOne(一条数据)
//查询 商品类型是手机的 只显示 id 和name 降序排序
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"
AppDataSource.initialize().then(async () => {
const goods = await AppDataSource.manager.findOne(Good, {
select: ["id", "name"], // 查询字段
where: { type: "手机" }, // 查询条件
order: { id: "DESC" }, // 排序 降序
});
console.log(goods);
}).catch(error => console.log(error))
//打印的数据
Good { id: 19, name: 'OPPO' }
这里排序没什么用,因为只有一条哈哈
findOne 只是比 find 多加了个 LIMIT 1,其余的都一样所以find可以用,看一下效果
// 查询 id是16.15的手机的 只显示 id 和name 降序排序
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"
import { In } from "typeorm"
AppDataSource.initialize().then(async () => {
const goods = await AppDataSource.manager.find(Good, {
select: ["id", "name"], // 查询字段
where: { id: In([16, 15]) }, // 查询条件
order: { id: "DESC" }, // 排序
});
console.log(goods);
}).catch(error => console.log(error))
//打印的数据 数据库没有id等于15的数据
[ Good { id: 16, name: '小米' } ]
findOneBy(后面直接对象写想查询的条件)
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"
AppDataSource.initialize().then(async () => {
const goods = await AppDataSource.manager.findOneBy(Good, {
id: 16
});
console.log(goods);
}).catch(error => console.log(error))
//查询出id是16的数据
Good { id: 16, name: '小米', type: '手机', price: '1000' }
query builder
- 遇到极其复杂的 我们使用这种方式,这里简举例
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"
AppDataSource.initialize().then(async () => {
const queryBuilder = await AppDataSource.manager.createQueryBuilder();
const goods = await queryBuilder.select("good").from(Good, "good")
.where("good.id = :id", { id: 16 })
.getOne()
console.log(goods);
}).catch(error => console.log(error))
//打印出的数据
Good { id: 16, name: '小米', type: '手机', price: '1000' }
transaction
-
多条有关联的数据的增删改都离不开事务
- 开启事务
// transaction 方法包裹下就好
await AppDataSource.manager.transaction(async manager => {
await manager.save(User, {
id: 4,
firstName: 'eee',
lastName: 'eee',
age: 20
});
});
简化增删改查
- 可以先调用 getRepository 传入 Entity,拿到专门处理这个 Entity 的增删改查的类,再调用这些方法
//以前是这样的
import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"
AppDataSource.initialize().then(async () => {
await AppDataSource.manager.save(Good, [
{ name: 'ccc', type: 'ccc', price: "21" },
{ name: 'ddd', type: 'ddd', price: "22" },
{ name: 'eee', type: 'eee', price: "23" }
])
}).catch(error => console.log(error))
//现在import { AppDataSource } from "./data-source"
import { Good } from "./entity/Good"
AppDataSource.initialize().then(async () => {
await AppDataSource.manager.getRepository(Good).方法
}).catch(error => console.log(error))