[{"data":1,"prerenderedAt":2365},["ShallowReactive",2],{"page-/post/nest/nest-from-typeorm-to-prisma":3,"surrounding-page":2356},{"id":4,"title":5,"author":6,"body":7,"date":2340,"description":2341,"extension":2342,"group":2343,"lastmod":2344,"meta":2345,"navigation":255,"path":2348,"rawbody":2349,"seo":2350,"showTitle":5,"stem":2351,"tags":2352,"versions":2343,"__hash__":2355},"content/post/Nest/nest-from-typeorm-to-prisma.md","Nest从TypeORM到Prisma：迁移记录","zzdaddy",{"type":8,"value":9,"toc":2332},"minimark",[10,14,18,21,51,54,70,81,97,100,118,125,131,292,295,351,354,360,366,372,379,427,430,443,448,451,460,474,517,522,539,546,673,688,691,703,706,722,725,760,763,785,788,791,795,798,809,831,835,845,938,949,955,970,975,994,997,1000,1226,1229,1246,1272,1275,1287,1319,1322,1327,1368,1371,1429,1436,1475,1478,1481,1484,1492,1518,1530,1819,1822,1825,1868,1871,1874,1877,1925,1928,1962,1965,1985,1988,2036,2039,2067,2070,2073,2076,2097,2117,2120,2133,2136,2255,2258,2270,2273,2276,2302,2305,2308,2311,2314,2322,2328],[11,12,13],"p",{},"最近把typeORM换成了prisma，主要是想用prisma的migrate功能，于是记录了一下其中发现的问题。",[15,16,17],"h3",{"id":17},"安装",[11,19,20],{},"全局安装prisma",[22,23,28],"pre",{"className":24,"code":25,"language":26,"meta":27,"style":27},"language-shell shiki shiki-themes github-light","npm i -g prisma\n","shell","",[29,30,31],"code",{"__ignoreMap":27},[32,33,36,40,44,48],"span",{"class":34,"line":35},"line",1,[32,37,39],{"class":38},"s7eDp","npm",[32,41,43],{"class":42},"sYBdl"," i",[32,45,47],{"class":46},"sYu0t"," -g",[32,49,50],{"class":42}," prisma\n",[11,52,53],{},"项目安装@prisma/client",[22,55,57],{"className":24,"code":56,"language":26,"meta":27,"style":27},"pnpm add @prisma/client\n",[29,58,59],{"__ignoreMap":27},[32,60,61,64,67],{"class":34,"line":35},[32,62,63],{"class":38},"pnpm",[32,65,66],{"class":42}," add",[32,68,69],{"class":42}," @prisma/client\n",[11,71,72,73,76,77,80],{},"在nest根目录初始化prisma，初始化后会在根目录创建一个prisma目录以及一个",[29,74,75],{},".env","，目录里面有个",[29,78,79],{},"schema.prisma"," 。",[22,82,84],{"className":24,"code":83,"language":26,"meta":27,"style":27},"npx prisma init\n",[29,85,86],{"__ignoreMap":27},[32,87,88,91,94],{"class":34,"line":35},[32,89,90],{"class":38},"npx",[32,92,93],{"class":42}," prisma",[32,95,96],{"class":42}," init\n",[11,98,99],{},"nest创建一个新的模块prisma",[22,101,103],{"className":24,"code":102,"language":26,"meta":27,"style":27},"nest g resource prisma\n",[29,104,105],{"__ignoreMap":27},[32,106,107,110,113,116],{"class":34,"line":35},[32,108,109],{"class":38},"nest",[32,111,112],{"class":42}," g",[32,114,115],{"class":42}," resource",[32,117,50],{"class":42},[11,119,120,121,124],{},"可以用",[29,122,123],{},"@Global","把这个模块注册成全局模块。",[11,126,127,130],{},[29,128,129],{},"service部分","可以参考nest官网的推荐或者其他野文，基本上都一样",[22,132,136],{"className":133,"code":134,"language":135,"meta":27,"style":27},"language-typescript shiki shiki-themes github-light","@Injectable()\nexport class PrismaService\n  extends PrismaClient\n  implements OnApplicationBootstrap, OnApplicationShutdown\n{\n  constructor() {\n    super();\n  }\n  async onApplicationBootstrap() {\n    await this.$connect();\n  }\n\n  async onApplicationShutdown() {\n    await this.$disconnect();\n  }\n}\n","typescript",[29,137,138,150,163,172,187,193,202,211,217,228,245,250,257,267,281,286],{"__ignoreMap":27},[32,139,140,144,147],{"class":34,"line":35},[32,141,143],{"class":142},"sgsFI","@",[32,145,146],{"class":38},"Injectable",[32,148,149],{"class":142},"()\n",[32,151,153,157,160],{"class":34,"line":152},2,[32,154,156],{"class":155},"sD7c4","export",[32,158,159],{"class":155}," class",[32,161,162],{"class":38}," PrismaService\n",[32,164,166,169],{"class":34,"line":165},3,[32,167,168],{"class":155},"  extends",[32,170,171],{"class":38}," PrismaClient\n",[32,173,175,178,181,184],{"class":34,"line":174},4,[32,176,177],{"class":155},"  implements",[32,179,180],{"class":38}," OnApplicationBootstrap",[32,182,183],{"class":142},", ",[32,185,186],{"class":38},"OnApplicationShutdown\n",[32,188,190],{"class":34,"line":189},5,[32,191,192],{"class":142},"{\n",[32,194,196,199],{"class":34,"line":195},6,[32,197,198],{"class":155},"  constructor",[32,200,201],{"class":142},"() {\n",[32,203,205,208],{"class":34,"line":204},7,[32,206,207],{"class":46},"    super",[32,209,210],{"class":142},"();\n",[32,212,214],{"class":34,"line":213},8,[32,215,216],{"class":142},"  }\n",[32,218,220,223,226],{"class":34,"line":219},9,[32,221,222],{"class":155},"  async",[32,224,225],{"class":38}," onApplicationBootstrap",[32,227,201],{"class":142},[32,229,231,234,237,240,243],{"class":34,"line":230},10,[32,232,233],{"class":155},"    await",[32,235,236],{"class":46}," this",[32,238,239],{"class":142},".",[32,241,242],{"class":38},"$connect",[32,244,210],{"class":142},[32,246,248],{"class":34,"line":247},11,[32,249,216],{"class":142},[32,251,253],{"class":34,"line":252},12,[32,254,256],{"emptyLinePlaceholder":255},true,"\n",[32,258,260,262,265],{"class":34,"line":259},13,[32,261,222],{"class":155},[32,263,264],{"class":38}," onApplicationShutdown",[32,266,201],{"class":142},[32,268,270,272,274,276,279],{"class":34,"line":269},14,[32,271,233],{"class":155},[32,273,236],{"class":46},[32,275,239],{"class":142},[32,277,278],{"class":38},"$disconnect",[32,280,210],{"class":142},[32,282,284],{"class":34,"line":283},15,[32,285,216],{"class":142},[32,287,289],{"class":34,"line":288},16,[32,290,291],{"class":142},"}\n",[11,293,294],{},"把之前typeORM相关的，find、save等等方法一一替换成prisma。比如：",[22,296,298],{"className":133,"code":297,"language":135,"meta":27,"style":27},"async findUserById(id: bigint) {\n    return this.prisma.user.findUnique({\n      where: {\n        id,\n      },\n    });\n  } \n",[29,299,300,311,327,332,337,342,347],{"__ignoreMap":27},[32,301,302,305,308],{"class":34,"line":35},[32,303,304],{"class":142},"async ",[32,306,307],{"class":38},"findUserById",[32,309,310],{"class":142},"(id: bigint) {\n",[32,312,313,316,318,321,324],{"class":34,"line":152},[32,314,315],{"class":155},"    return",[32,317,236],{"class":46},[32,319,320],{"class":142},".prisma.user.",[32,322,323],{"class":38},"findUnique",[32,325,326],{"class":142},"({\n",[32,328,329],{"class":34,"line":165},[32,330,331],{"class":142},"      where: {\n",[32,333,334],{"class":34,"line":174},[32,335,336],{"class":142},"        id,\n",[32,338,339],{"class":34,"line":189},[32,340,341],{"class":142},"      },\n",[32,343,344],{"class":34,"line":195},[32,345,346],{"class":142},"    });\n",[32,348,349],{"class":34,"line":204},[32,350,216],{"class":142},[11,352,353],{},"确定没有问题之后，看一下prisma的常用命令。这部分也可以先跳过，migrate dev命令放在了正确的迁移这一小节中。",[11,355,356,359],{},[29,357,358],{},"db pull ","\n基于数据库schema生成prisma schema",[11,361,362,365],{},[29,363,364],{},"prisma generate","\n生成client代码，不会同步数据库",[11,367,368,371],{},[29,369,370],{},"prisma db push","\n用于将本地的表结构改动同步到数据库。",[11,373,374,375,378],{},"如果表里已经有了字段和数据，再新增字段时，务必设置为允许为null的可选字段，如",[29,376,377],{},"String?","，否则会清空表结构。虽然会提示你，也不排除很多人会不看英文提示。",[22,380,382],{"className":24,"code":381,"language":26,"meta":27,"style":27}," username   String?  @db.VarChar(255) ✅\n username2  String  @db.VarChar(255)  ❌\n",[29,383,384,407],{"__ignoreMap":27},[32,385,386,389,392,395,398,401,404],{"class":34,"line":35},[32,387,388],{"class":38}," username",[32,390,391],{"class":42},"   String?",[32,393,394],{"class":42},"  @db.VarChar",[32,396,397],{"class":142},"(",[32,399,400],{"class":38},"255",[32,402,403],{"class":142},") ",[32,405,406],{"class":42},"✅\n",[32,408,409,412,415,417,419,421,424],{"class":34,"line":152},[32,410,411],{"class":38}," username2",[32,413,414],{"class":42},"  String",[32,416,394],{"class":42},[32,418,397],{"class":142},[32,420,400],{"class":38},[32,422,423],{"class":142},")  ",[32,425,426],{"class":42},"❌\n",[11,428,429],{},"如果是删除了字段。运行后会同步到mysql数据库中。同时不会删除已有的数据，只会删除对应的字段。",[11,431,432,442],{},[433,434,439],"a",{"href":435,"rel":436,"title":438},"https://www.prisma.io/docs/orm/reference/prisma-cli-reference#migrate-resolve",[437],"nofollow","Prisma CLI reference",[440,441,438],"strong",{}," 👈此处有所有命令，还是建议看看官方文档。",[11,444,445],{},[440,446,447],{},"注意此操作不会像migrate一样会被记录",[15,449,450],{"id":450},"正确的迁移",[11,452,453,454,459],{},"迁移过程也有",[433,455,458],{"href":456,"rel":457,"title":458},"https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project",[437],"官方文档","👈，这里相当于我实践后又讲述了一遍。",[461,462,463],"ol",{},[464,465,466,467,470,471,473],"li",{},"首先设置好开头",[29,468,469],{},"init","好的",[29,472,79],{},"，如：",[22,475,477],{"className":133,"code":476,"language":135,"meta":27,"style":27},"datasource db {\n  provider = \"mysql\"\n  url      = env(\"DATABASE_URL\")\n}\n",[29,478,479,484,495,513],{"__ignoreMap":27},[32,480,481],{"class":34,"line":35},[32,482,483],{"class":142},"datasource db {\n",[32,485,486,489,492],{"class":34,"line":152},[32,487,488],{"class":142},"  provider ",[32,490,491],{"class":155},"=",[32,493,494],{"class":42}," \"mysql\"\n",[32,496,497,500,502,505,507,510],{"class":34,"line":165},[32,498,499],{"class":142},"  url      ",[32,501,491],{"class":155},[32,503,504],{"class":38}," env",[32,506,397],{"class":142},[32,508,509],{"class":42},"\"DATABASE_URL\"",[32,511,512],{"class":142},")\n",[32,514,515],{"class":34,"line":174},[32,516,291],{"class":142},[461,518,519],{},[464,520,521],{},"使用dotenv重新配置一下env文件，和项目现有的env文件结合起来，如：",[22,523,525],{"className":24,"code":524,"language":26,"meta":27,"style":27},"npm install -g dotenv-cli\n",[29,526,527],{"__ignoreMap":27},[32,528,529,531,534,536],{"class":34,"line":35},[32,530,39],{"class":38},[32,532,533],{"class":42}," install",[32,535,47],{"class":46},[32,537,538],{"class":42}," dotenv-cli\n",[11,540,541,542,545],{},"在",[29,543,544],{},"package.json","配置一下常用命令",[22,547,549],{"className":133,"code":548,"language":135,"meta":27,"style":27},"  \"migrate:dev\": \"npx dotenv -e .dev.env -- prisma migrate dev\",\n    \"migrate:deploy\": \"npx dotenv -e .prod.env -- prisma migrate deploy\",\n    \"prisma:generate:dev\": \"npx dotenv -e .dev.env -- prisma generate\",\n    \"prisma:generate:prod\": \"npx dotenv -e .prod.env -- prisma generate\",\n    \"db:push:dev\": \"npx dotenv -e .dev.env -- prisma db push\",\n    \"db:push:prod\": \"npx dotenv -e .prod.env -- prisma db push\",\n    \"db:pull:dev\": \"npx dotenv -e .dev.env -- prisma db pull\",\n    \"db:pull:prod\": \"npx dotenv -e .prod.env -- prisma db pull\",\n    \"db:seed:dev\": \"npx dotenv -e .dev.env -- prisma db seed\",\n    \"db:seed:prod\": \"npx dotenv -e .prod.env -- prisma db seed\",\n",[29,550,551,565,577,589,601,613,625,637,649,661],{"__ignoreMap":27},[32,552,553,556,559,562],{"class":34,"line":35},[32,554,555],{"class":42},"  \"migrate:dev\"",[32,557,558],{"class":142},": ",[32,560,561],{"class":42},"\"npx dotenv -e .dev.env -- prisma migrate dev\"",[32,563,564],{"class":142},",\n",[32,566,567,570,572,575],{"class":34,"line":152},[32,568,569],{"class":42},"    \"migrate:deploy\"",[32,571,558],{"class":142},[32,573,574],{"class":42},"\"npx dotenv -e .prod.env -- prisma migrate deploy\"",[32,576,564],{"class":142},[32,578,579,582,584,587],{"class":34,"line":165},[32,580,581],{"class":42},"    \"prisma:generate:dev\"",[32,583,558],{"class":142},[32,585,586],{"class":42},"\"npx dotenv -e .dev.env -- prisma generate\"",[32,588,564],{"class":142},[32,590,591,594,596,599],{"class":34,"line":174},[32,592,593],{"class":42},"    \"prisma:generate:prod\"",[32,595,558],{"class":142},[32,597,598],{"class":42},"\"npx dotenv -e .prod.env -- prisma generate\"",[32,600,564],{"class":142},[32,602,603,606,608,611],{"class":34,"line":189},[32,604,605],{"class":42},"    \"db:push:dev\"",[32,607,558],{"class":142},[32,609,610],{"class":42},"\"npx dotenv -e .dev.env -- prisma db push\"",[32,612,564],{"class":142},[32,614,615,618,620,623],{"class":34,"line":195},[32,616,617],{"class":42},"    \"db:push:prod\"",[32,619,558],{"class":142},[32,621,622],{"class":42},"\"npx dotenv -e .prod.env -- prisma db push\"",[32,624,564],{"class":142},[32,626,627,630,632,635],{"class":34,"line":204},[32,628,629],{"class":42},"    \"db:pull:dev\"",[32,631,558],{"class":142},[32,633,634],{"class":42},"\"npx dotenv -e .dev.env -- prisma db pull\"",[32,636,564],{"class":142},[32,638,639,642,644,647],{"class":34,"line":213},[32,640,641],{"class":42},"    \"db:pull:prod\"",[32,643,558],{"class":142},[32,645,646],{"class":42},"\"npx dotenv -e .prod.env -- prisma db pull\"",[32,648,564],{"class":142},[32,650,651,654,656,659],{"class":34,"line":219},[32,652,653],{"class":42},"    \"db:seed:dev\"",[32,655,558],{"class":142},[32,657,658],{"class":42},"\"npx dotenv -e .dev.env -- prisma db seed\"",[32,660,564],{"class":142},[32,662,663,666,668,671],{"class":34,"line":230},[32,664,665],{"class":42},"    \"db:seed:prod\"",[32,667,558],{"class":142},[32,669,670],{"class":42},"\"npx dotenv -e .prod.env -- prisma db seed\"",[32,672,564],{"class":142},[11,674,675,676,679,680,683,684,687],{},"然后把prisma默认的env里配置的",[29,677,678],{},"DATABASE_URL","挪到",[29,681,682],{},".dev.env"," 和",[29,685,686],{}," .prod.env","里去，在不同环境测试一下能否正确连接数据库。",[11,689,690],{},"由于数据库里已有表结构和数据，prisma属于后来者，我不想丢失数据和结构，所以先使用db pull 生成prisma schema",[22,692,694],{"className":24,"code":693,"language":26,"meta":27,"style":27},"pnpm db:pull:dev\n",[29,695,696],{"__ignoreMap":27},[32,697,698,700],{"class":34,"line":35},[32,699,63],{"class":38},[32,701,702],{"class":42}," db:pull:dev\n",[11,704,705],{},"最关键的一点来了。migrate dev会记录迁移的过程，如果直接使用migrate dev的话，他会清空数据，并生成一个migration和建表的sql，这样显然不符合预期。所以先手动创建一个migration",[22,707,709],{"className":24,"code":708,"language":26,"meta":27,"style":27},"mkdir -p prisma/migrations/0_init\n",[29,710,711],{"__ignoreMap":27},[32,712,713,716,719],{"class":34,"line":35},[32,714,715],{"class":38},"mkdir",[32,717,718],{"class":46}," -p",[32,720,721],{"class":42}," prisma/migrations/0_init\n",[11,723,724],{},"然后使用prisma migrate diff 输出一个sql脚本",[22,726,728],{"className":24,"code":727,"language":26,"meta":27,"style":27},"npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script > prisma/migrations/0_init/migration.sql\n",[29,729,730],{"__ignoreMap":27},[32,731,732,734,736,739,742,745,748,751,754,757],{"class":34,"line":35},[32,733,90],{"class":38},[32,735,93],{"class":42},[32,737,738],{"class":42}," migrate",[32,740,741],{"class":42}," diff",[32,743,744],{"class":46}," --from-empty",[32,746,747],{"class":46}," --to-schema-datamodel",[32,749,750],{"class":42}," prisma/schema.prisma",[32,752,753],{"class":46}," --script",[32,755,756],{"class":155}," >",[32,758,759],{"class":42}," prisma/migrations/0_init/migration.sql\n",[11,761,762],{},"此时，检查一下生成sql语句确保没有任何问题，然后把这个migration标记为已应用",[22,764,766],{"className":24,"code":765,"language":26,"meta":27,"style":27},"npx prisma migrate resolve --applied 0_init\n",[29,767,768],{"__ignoreMap":27},[32,769,770,772,774,776,779,782],{"class":34,"line":35},[32,771,90],{"class":38},[32,773,93],{"class":42},[32,775,738],{"class":42},[32,777,778],{"class":42}," resolve",[32,780,781],{"class":46}," --applied",[32,783,784],{"class":42}," 0_init\n",[11,786,787],{},"这个时候，去修改prisma schema后再使用migrate dev，就会生成基于已有数据库后的一个变更了。这个变更就不会影响已有数据了。",[11,789,790],{},"下边我也基于migrate dev的操作做了一些场景重现，感兴趣的可以瞅瞅。",[15,792,794],{"id":793},"migrate-dev-操作演示","migrate dev 操作演示",[11,796,797],{},"假设我不懂如何在两个环境之间迁移，只是在本地把玩prisma，直到我开始想到和线上数据库要同步一下。",[11,799,800,801,804,805,808],{},"运行",[29,802,803],{},"pnpm migrate:dev","会在prisma目录下生成一个",[29,806,807],{},"migrations","目录，并生成本次操作的sql。",[11,810,811,812,814,815,818,819,822,823,826,827,830],{},"现在本地数据库已经有了一堆表，刚刚接入了prisma，或者本地乱搞的时候已经不知道是什么状态了。我通过",[29,813,469],{},"后用",[29,816,817],{},"db puill ","生成了",[29,820,821],{},"prisma schame"," 然后我在",[29,824,825],{},"prisma schema","中删除了一个",[29,828,829],{},"model","。",[832,833,834],"h4",{"id":834},"删除表",[11,836,837,838,840,841,844],{},"然后运行",[29,839,803],{}," 并在提示下命名为",[29,842,843],{},"delete_table1","。它会提示你的数据库架构与迁移历史不一致，然后继续执行。因为此时我本地的状态已经混乱，migrations记录已经不在了，所以他从头开始给我记录了。",[22,846,848],{"className":133,"code":847,"language":135,"meta":27,"style":27},"Drift detected: Your database schema is not in sync with your migration history.\nThe following is a summary of the differences between the expected database schema given your migrations files, and the actual schema of the database.\nIt should be understood as the set of changes to get from the expected schema to the actual schema.\n\n",[29,849,850,873,889],{"__ignoreMap":27},[32,851,852,855,858,861,864,867,870],{"class":34,"line":35},[32,853,854],{"class":142},"Drift ",[32,856,857],{"class":38},"detected",[32,859,860],{"class":142},": Your database schema is not ",[32,862,863],{"class":155},"in",[32,865,866],{"class":142}," sync ",[32,868,869],{"class":155},"with",[32,871,872],{"class":142}," your migration history.\n",[32,874,875,878,881,884,886],{"class":34,"line":152},[32,876,877],{"class":142},"The following is a summary ",[32,879,880],{"class":155},"of",[32,882,883],{"class":142}," the differences between the expected database schema given your migrations files, and the actual schema ",[32,885,880],{"class":155},[32,887,888],{"class":142}," the database.\n",[32,890,891,894,897,900,903,906,909,912,915,918,920,923,926,928,930,933,935],{"class":34,"line":165},[32,892,893],{"class":142},"It should be understood ",[32,895,896],{"class":155},"as",[32,898,899],{"class":38}," the",[32,901,902],{"class":38}," set",[32,904,905],{"class":38}," of",[32,907,908],{"class":38}," changes",[32,910,911],{"class":38}," to",[32,913,914],{"class":38}," get",[32,916,917],{"class":38}," from",[32,919,899],{"class":38},[32,921,922],{"class":38}," expected",[32,924,925],{"class":38}," schema",[32,927,911],{"class":38},[32,929,899],{"class":38},[32,931,932],{"class":38}," actual",[32,934,925],{"class":38},[32,936,937],{"class":142},".\n",[11,939,940,941,944,945,948],{},"此时可以看到目录下生成对应的sql，",[440,942,943],{},"里面是一堆创建表的SQL语句","。再去本地数据库看的时候，数据都被清除了，并且自动执行了",[29,946,947],{},"seed.ts"," 插入了假数据",[11,950,951],{},[952,953],"img",{"alt":27,"src":954},"https://img.zzao.club/article/202411191448363.png",[11,956,957,958,961,962,965,966,969],{},"然后再删除一张表",[29,959,960],{},"admin_user","，然后再运行一下",[29,963,964],{},"migrate:dev","，这次命名为",[29,967,968],{},"delete_table2","，可以看到这次没有提示错误，只是单纯的删除了一张表",[11,971,972],{},[952,973],{"alt":27,"src":974},"https://img.zzao.club/article/202411191448364.png",[22,976,978],{"className":133,"code":977,"language":135,"meta":27,"style":27},"DROP TABLE `admin_user`;\n",[29,979,980],{"__ignoreMap":27},[32,981,982,985,988,991],{"class":34,"line":35},[32,983,984],{"class":46},"DROP",[32,986,987],{"class":46}," TABLE",[32,989,990],{"class":42}," `admin_user`",[32,992,993],{"class":142},";\n",[832,995,996],{"id":996},"增加表",[11,998,999],{},"这个没有什么可说的",[22,1001,1003],{"className":133,"code":1002,"language":135,"meta":27,"style":27},"CREATE TABLE `color2` (\n    `id` INTEGER NOT NULL AUTO_INCREMENT,\n    `desc` VARCHAR(20) NULL,\n    `desc2` VARCHAR(20) NULL,\n    `desc3` VARCHAR(20) NULL,\n    `value` VARCHAR(255) NOT NULL,\n    `createTime` DATETIME(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),\n    `updateTime` DATETIME(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),\n    `userId` BIGINT NOT NULL,\n\n    PRIMARY KEY (`id`)\n) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n",[29,1004,1005,1018,1037,1057,1074,1091,1111,1143,1170,1184,1188,1204],{"__ignoreMap":27},[32,1006,1007,1010,1012,1015],{"class":34,"line":35},[32,1008,1009],{"class":46},"CREATE",[32,1011,987],{"class":46},[32,1013,1014],{"class":42}," `color2`",[32,1016,1017],{"class":142}," (\n",[32,1019,1020,1023,1026,1029,1032,1035],{"class":34,"line":152},[32,1021,1022],{"class":42},"    `id`",[32,1024,1025],{"class":46}," INTEGER",[32,1027,1028],{"class":46}," NOT",[32,1030,1031],{"class":46}," NULL",[32,1033,1034],{"class":46}," AUTO_INCREMENT",[32,1036,564],{"class":142},[32,1038,1039,1042,1045,1047,1050,1052,1055],{"class":34,"line":165},[32,1040,1041],{"class":42},"    `desc`",[32,1043,1044],{"class":38}," VARCHAR",[32,1046,397],{"class":142},[32,1048,1049],{"class":46},"20",[32,1051,403],{"class":142},[32,1053,1054],{"class":46},"NULL",[32,1056,564],{"class":142},[32,1058,1059,1062,1064,1066,1068,1070,1072],{"class":34,"line":174},[32,1060,1061],{"class":42},"    `desc2`",[32,1063,1044],{"class":38},[32,1065,397],{"class":142},[32,1067,1049],{"class":46},[32,1069,403],{"class":142},[32,1071,1054],{"class":46},[32,1073,564],{"class":142},[32,1075,1076,1079,1081,1083,1085,1087,1089],{"class":34,"line":189},[32,1077,1078],{"class":42},"    `desc3`",[32,1080,1044],{"class":38},[32,1082,397],{"class":142},[32,1084,1049],{"class":46},[32,1086,403],{"class":142},[32,1088,1054],{"class":46},[32,1090,564],{"class":142},[32,1092,1093,1096,1098,1100,1102,1104,1107,1109],{"class":34,"line":195},[32,1094,1095],{"class":42},"    `value`",[32,1097,1044],{"class":38},[32,1099,397],{"class":142},[32,1101,400],{"class":46},[32,1103,403],{"class":142},[32,1105,1106],{"class":46},"NOT",[32,1108,1031],{"class":46},[32,1110,564],{"class":142},[32,1112,1113,1116,1119,1121,1124,1126,1128,1130,1133,1136,1138,1140],{"class":34,"line":204},[32,1114,1115],{"class":42},"    `createTime`",[32,1117,1118],{"class":38}," DATETIME",[32,1120,397],{"class":142},[32,1122,1123],{"class":46},"0",[32,1125,403],{"class":142},[32,1127,1106],{"class":46},[32,1129,1031],{"class":46},[32,1131,1132],{"class":46}," DEFAULT",[32,1134,1135],{"class":38}," CURRENT_TIMESTAMP",[32,1137,397],{"class":142},[32,1139,1123],{"class":46},[32,1141,1142],{"class":142},"),\n",[32,1144,1145,1148,1150,1152,1154,1156,1158,1160,1162,1164,1166,1168],{"class":34,"line":213},[32,1146,1147],{"class":42},"    `updateTime`",[32,1149,1118],{"class":38},[32,1151,397],{"class":142},[32,1153,1123],{"class":46},[32,1155,403],{"class":142},[32,1157,1106],{"class":46},[32,1159,1031],{"class":46},[32,1161,1132],{"class":46},[32,1163,1135],{"class":38},[32,1165,397],{"class":142},[32,1167,1123],{"class":46},[32,1169,1142],{"class":142},[32,1171,1172,1175,1178,1180,1182],{"class":34,"line":219},[32,1173,1174],{"class":42},"    `userId`",[32,1176,1177],{"class":46}," BIGINT",[32,1179,1028],{"class":46},[32,1181,1031],{"class":46},[32,1183,564],{"class":142},[32,1185,1186],{"class":34,"line":230},[32,1187,256],{"emptyLinePlaceholder":255},[32,1189,1190,1193,1196,1199,1202],{"class":34,"line":247},[32,1191,1192],{"class":46},"    PRIMARY",[32,1194,1195],{"class":38}," KEY",[32,1197,1198],{"class":142}," (",[32,1200,1201],{"class":42},"`id`",[32,1203,512],{"class":142},[32,1205,1206,1208,1211,1214,1217,1220,1223],{"class":34,"line":252},[32,1207,403],{"class":142},[32,1209,1210],{"class":46},"DEFAULT",[32,1212,1213],{"class":46}," CHARACTER",[32,1215,1216],{"class":46}," SET",[32,1218,1219],{"class":142}," utf8mb4 ",[32,1221,1222],{"class":46},"COLLATE",[32,1224,1225],{"class":142}," utf8mb4_unicode_ci;\n",[832,1227,1228],{"id":1228},"删除字段",[11,1230,1231,1232,1235,1236,1238,1239,1242,1243,1245],{},"然后我找了一张表",[29,1233,1234],{},"color","，给他直接加了一条数据，然后在",[29,1237,829],{},"里把其中一个字段",[29,1240,1241],{},"desc","删掉，再去运行",[29,1244,803],{},"，这次生成的SQL里也只是删除了一个字段，对数据没有影响。",[22,1247,1249],{"className":133,"code":1248,"language":135,"meta":27,"style":27},"ALTER TABLE `color` DROP COLUMN `desc`;\n",[29,1250,1251],{"__ignoreMap":27},[32,1252,1253,1256,1258,1261,1264,1267,1270],{"class":34,"line":35},[32,1254,1255],{"class":46},"ALTER",[32,1257,987],{"class":46},[32,1259,1260],{"class":42}," `color`",[32,1262,1263],{"class":46}," DROP",[32,1265,1266],{"class":46}," COLUMN",[32,1268,1269],{"class":42}," `desc`",[32,1271,993],{"class":142},[832,1273,1274],{"id":1274},"增加字段",[11,1276,1277,1278,1280,1281,1283,1284],{},"还是这张表",[29,1279,1234],{},"，再加两个必填字段",[29,1282,1241],{},"、",[29,1285,1286],{},"desc2",[22,1288,1290],{"className":133,"code":1289,"language":135,"meta":27,"style":27},"desc       String   @db.VarChar(20)\ndesc2      String   @db.VarChar(20)\n",[29,1291,1292,1306],{"__ignoreMap":27},[32,1293,1294,1297,1300,1302,1304],{"class":34,"line":35},[32,1295,1296],{"class":142},"desc       String   @db.",[32,1298,1299],{"class":38},"VarChar",[32,1301,397],{"class":142},[32,1303,1049],{"class":46},[32,1305,512],{"class":142},[32,1307,1308,1311,1313,1315,1317],{"class":34,"line":152},[32,1309,1310],{"class":142},"desc2      String   @db.",[32,1312,1299],{"class":38},[32,1314,397],{"class":142},[32,1316,1049],{"class":46},[32,1318,512],{"class":142},[11,1320,1321],{},"因为这俩字段都是必填的，所以直接把表清空了，这次也没提示会清空表（？）。",[11,1323,1324,1325],{},"再去数据造一条数据，然后再把这俩字段改成可选，再次执行",[29,1326,803],{},[22,1328,1330],{"className":133,"code":1329,"language":135,"meta":27,"style":27},"desc       String?   @db.VarChar(20)\ndesc2      String?   @db.VarChar(20)\n",[29,1331,1332,1351],{"__ignoreMap":27},[32,1333,1334,1337,1340,1343,1345,1347,1349],{"class":34,"line":35},[32,1335,1336],{"class":142},"desc       String",[32,1338,1339],{"class":155},"?",[32,1341,1342],{"class":142},"   @db.",[32,1344,1299],{"class":38},[32,1346,397],{"class":142},[32,1348,1049],{"class":46},[32,1350,512],{"class":142},[32,1352,1353,1356,1358,1360,1362,1364,1366],{"class":34,"line":152},[32,1354,1355],{"class":142},"desc2      String",[32,1357,1339],{"class":155},[32,1359,1342],{"class":142},[32,1361,1299],{"class":38},[32,1363,397],{"class":142},[32,1365,1049],{"class":46},[32,1367,512],{"class":142},[11,1369,1370],{},"这次没有数据没有被清，只是字段被设置了可以为null",[22,1372,1374],{"className":133,"code":1373,"language":135,"meta":27,"style":27},"-- AlterTable\nALTER TABLE `color` MODIFY `desc` VARCHAR(20) NULL,\n    MODIFY `desc2` VARCHAR(20) NULL;\n\n",[29,1375,1376,1384,1409],{"__ignoreMap":27},[32,1377,1378,1381],{"class":34,"line":35},[32,1379,1380],{"class":155},"--",[32,1382,1383],{"class":142}," AlterTable\n",[32,1385,1386,1388,1390,1392,1395,1397,1399,1401,1403,1405,1407],{"class":34,"line":152},[32,1387,1255],{"class":46},[32,1389,987],{"class":46},[32,1391,1260],{"class":42},[32,1393,1394],{"class":46}," MODIFY",[32,1396,1269],{"class":42},[32,1398,1044],{"class":38},[32,1400,397],{"class":142},[32,1402,1049],{"class":46},[32,1404,403],{"class":142},[32,1406,1054],{"class":46},[32,1408,564],{"class":142},[32,1410,1411,1414,1417,1419,1421,1423,1425,1427],{"class":34,"line":165},[32,1412,1413],{"class":46},"    MODIFY",[32,1415,1416],{"class":42}," `desc2`",[32,1418,1044],{"class":38},[32,1420,397],{"class":142},[32,1422,1049],{"class":46},[32,1424,403],{"class":142},[32,1426,1054],{"class":46},[32,1428,993],{"class":142},[11,1430,1431,1432,1435],{},"直接添加一个可选字段",[29,1433,1434],{},"desc3","，这次只是单纯增加了一个可以为null的desc3字段",[22,1437,1439],{"className":133,"code":1438,"language":135,"meta":27,"style":27},"-- AlterTable\nALTER TABLE `color` ADD COLUMN `desc3` VARCHAR(20) NULL;\n",[29,1440,1441,1447],{"__ignoreMap":27},[32,1442,1443,1445],{"class":34,"line":35},[32,1444,1380],{"class":155},[32,1446,1383],{"class":142},[32,1448,1449,1451,1453,1455,1458,1460,1463,1465,1467,1469,1471,1473],{"class":34,"line":152},[32,1450,1255],{"class":46},[32,1452,987],{"class":46},[32,1454,1260],{"class":42},[32,1456,1457],{"class":46}," ADD",[32,1459,1266],{"class":46},[32,1461,1462],{"class":42}," `desc3`",[32,1464,1044],{"class":38},[32,1466,397],{"class":142},[32,1468,1049],{"class":46},[32,1470,403],{"class":142},[32,1472,1054],{"class":46},[32,1474,993],{"class":142},[11,1476,1477],{},"正常情况下，已经有数据的表，不可能再增加一个必填字段了，因为已有字段就都不符合条件了。我这里只是演示一下，对于一个没接触过后端和数据库的前端来说，这个可能也是需要考虑的......",[832,1479,1480],{"id":1480},"同步到其他环境的数据库",[11,1482,1483],{},"如果本地有docker容器的端口会冲突的话，先stop一下，然后把nest项目本地用docker-compose跑起来。",[11,1485,1486,1487,830],{},"如何使用docker-compose 部署nest项目可以看下我这篇文章，里面有详细解释 👉",[433,1488,1491],{"href":1489,"rel":1490,"title":1491},"https://juejin.cn/post/7316202589603807259",[437],"使用 Docker Compose 部署 Nest 应用",[22,1493,1495],{"className":133,"code":1494,"language":135,"meta":27,"style":27},"docker-compose up --build -d\n",[29,1496,1497],{"__ignoreMap":27},[32,1498,1499,1502,1505,1508,1510,1513,1515],{"class":34,"line":35},[32,1500,1501],{"class":142},"docker",[32,1503,1504],{"class":155},"-",[32,1506,1507],{"class":142},"compose up ",[32,1509,1380],{"class":155},[32,1511,1512],{"class":142},"build ",[32,1514,1504],{"class":155},[32,1516,1517],{"class":142},"d\n",[11,1519,1520,1521,1524,1525,683,1527,1529],{},"打包完成后，来到nest服务内部，这里我用的Docker Desktop 演示。此时，正式数据库还是没有",[29,1522,1523],{},"color2","这个表，color表里也没有",[29,1526,1286],{},[29,1528,1241],{},"的。然后运行一下，为了保证他运行不报错，我先把20240117080859_delete_table1删了，因为这个sql里全是建表的语句。后面会重新来一下这个过程来弥补这个错误。",[22,1531,1533],{"className":133,"code":1532,"language":135,"meta":27,"style":27},"/app # pnpm migrate:deploy\n\n> master@0.0.1 migrate:deploy /app\n> npx dotenv -e .prod.env -- prisma migrate deploy\n\nPrisma schema loaded from prisma/schema.prisma\nDatasource \"db\": MySQL database \"zzstudio\" at \"zz_mysql:3306\"\n\n6 migrations found in prisma/migrations\n\nApplying migration `20240117080859_delete_table2`\nApplying migration `20240117082030_delete_field1`\nApplying migration `20240117082351_add_desc2`\nApplying migration `20240117082752_optional1`\nApplying migration `20240117083056_add_desc3`\nApplying migration `20240117083359_addtablecolor2`\n\nThe following migrations have been applied:\n\nmigrations/\n  └─ 20240117080859_delete_table2/\n    └─ migration.sql\n  └─ 20240117082030_delete_field1/\n    └─ migration.sql\n  └─ 20240117082351_add_desc2/\n    └─ migration.sql\n  └─ 20240117082752_optional1/\n    └─ migration.sql\n  └─ 20240117083056_add_desc3/\n    └─ migration.sql\n  └─ 20240117083359_addtablecolor2/\n    └─ migration.sql\n      \nAll migrations have been successfully applied.\n\n",[29,1534,1535,1549,1553,1579,1596,1600,1610,1630,1634,1651,1655,1663,1670,1677,1684,1691,1698,1703,1715,1720,1728,1736,1742,1750,1755,1763,1768,1776,1781,1789,1794,1802,1807,1813],{"__ignoreMap":27},[32,1536,1537,1540,1543,1546],{"class":34,"line":35},[32,1538,1539],{"class":155},"/",[32,1541,1542],{"class":142},"app # pnpm ",[32,1544,1545],{"class":38},"migrate",[32,1547,1548],{"class":142},":deploy\n",[32,1550,1551],{"class":34,"line":152},[32,1552,256],{"emptyLinePlaceholder":255},[32,1554,1555,1558,1561,1564,1566,1569,1571,1574,1576],{"class":34,"line":165},[32,1556,1557],{"class":155},">",[32,1559,1560],{"class":142}," master@",[32,1562,1563],{"class":46},"0.0",[32,1565,239],{"class":142},[32,1567,1568],{"class":46},"1",[32,1570,738],{"class":38},[32,1572,1573],{"class":142},":deploy ",[32,1575,1539],{"class":155},[32,1577,1578],{"class":142},"app\n",[32,1580,1581,1583,1586,1588,1591,1593],{"class":34,"line":174},[32,1582,1557],{"class":155},[32,1584,1585],{"class":142}," npx dotenv ",[32,1587,1504],{"class":155},[32,1589,1590],{"class":142},"e .prod.env ",[32,1592,1380],{"class":155},[32,1594,1595],{"class":142}," prisma migrate deploy\n",[32,1597,1598],{"class":34,"line":189},[32,1599,256],{"emptyLinePlaceholder":255},[32,1601,1602,1605,1607],{"class":34,"line":195},[32,1603,1604],{"class":142},"Prisma schema loaded from prisma",[32,1606,1539],{"class":155},[32,1608,1609],{"class":142},"schema.prisma\n",[32,1611,1612,1615,1618,1621,1624,1627],{"class":34,"line":204},[32,1613,1614],{"class":142},"Datasource ",[32,1616,1617],{"class":42},"\"db\"",[32,1619,1620],{"class":142},": MySQL database ",[32,1622,1623],{"class":42},"\"zzstudio\"",[32,1625,1626],{"class":142}," at ",[32,1628,1629],{"class":42},"\"zz_mysql:3306\"\n",[32,1631,1632],{"class":34,"line":213},[32,1633,256],{"emptyLinePlaceholder":255},[32,1635,1636,1639,1642,1644,1646,1648],{"class":34,"line":219},[32,1637,1638],{"class":46},"6",[32,1640,1641],{"class":142}," migrations found ",[32,1643,863],{"class":155},[32,1645,93],{"class":142},[32,1647,1539],{"class":155},[32,1649,1650],{"class":142},"migrations\n",[32,1652,1653],{"class":34,"line":230},[32,1654,256],{"emptyLinePlaceholder":255},[32,1656,1657,1660],{"class":34,"line":247},[32,1658,1659],{"class":142},"Applying migration ",[32,1661,1662],{"class":42},"`20240117080859_delete_table2`\n",[32,1664,1665,1667],{"class":34,"line":252},[32,1666,1659],{"class":142},[32,1668,1669],{"class":42},"`20240117082030_delete_field1`\n",[32,1671,1672,1674],{"class":34,"line":259},[32,1673,1659],{"class":142},[32,1675,1676],{"class":42},"`20240117082351_add_desc2`\n",[32,1678,1679,1681],{"class":34,"line":269},[32,1680,1659],{"class":142},[32,1682,1683],{"class":42},"`20240117082752_optional1`\n",[32,1685,1686,1688],{"class":34,"line":283},[32,1687,1659],{"class":142},[32,1689,1690],{"class":42},"`20240117083056_add_desc3`\n",[32,1692,1693,1695],{"class":34,"line":288},[32,1694,1659],{"class":142},[32,1696,1697],{"class":42},"`20240117083359_addtablecolor2`\n",[32,1699,1701],{"class":34,"line":1700},17,[32,1702,256],{"emptyLinePlaceholder":255},[32,1704,1706,1709,1712],{"class":34,"line":1705},18,[32,1707,1708],{"class":142},"The following migrations have been ",[32,1710,1711],{"class":38},"applied",[32,1713,1714],{"class":142},":\n",[32,1716,1718],{"class":34,"line":1717},19,[32,1719,256],{"emptyLinePlaceholder":255},[32,1721,1723,1725],{"class":34,"line":1722},20,[32,1724,807],{"class":142},[32,1726,1727],{"class":155},"/\n",[32,1729,1731,1734],{"class":34,"line":1730},21,[32,1732,1733],{"class":142},"  └─ 20240117080859_delete_table2",[32,1735,1727],{"class":155},[32,1737,1739],{"class":34,"line":1738},22,[32,1740,1741],{"class":142},"    └─ migration.sql\n",[32,1743,1745,1748],{"class":34,"line":1744},23,[32,1746,1747],{"class":142},"  └─ 20240117082030_delete_field1",[32,1749,1727],{"class":155},[32,1751,1753],{"class":34,"line":1752},24,[32,1754,1741],{"class":142},[32,1756,1758,1761],{"class":34,"line":1757},25,[32,1759,1760],{"class":142},"  └─ 20240117082351_add_desc2",[32,1762,1727],{"class":155},[32,1764,1766],{"class":34,"line":1765},26,[32,1767,1741],{"class":142},[32,1769,1771,1774],{"class":34,"line":1770},27,[32,1772,1773],{"class":142},"  └─ 20240117082752_optional1",[32,1775,1727],{"class":155},[32,1777,1779],{"class":34,"line":1778},28,[32,1780,1741],{"class":142},[32,1782,1784,1787],{"class":34,"line":1783},29,[32,1785,1786],{"class":142},"  └─ 20240117083056_add_desc3",[32,1788,1727],{"class":155},[32,1790,1792],{"class":34,"line":1791},30,[32,1793,1741],{"class":142},[32,1795,1797,1800],{"class":34,"line":1796},31,[32,1798,1799],{"class":142},"  └─ 20240117083359_addtablecolor2",[32,1801,1727],{"class":155},[32,1803,1805],{"class":34,"line":1804},32,[32,1806,1741],{"class":142},[32,1808,1810],{"class":34,"line":1809},33,[32,1811,1812],{"class":142},"      \n",[32,1814,1816],{"class":34,"line":1815},34,[32,1817,1818],{"class":142},"All migrations have been successfully applied.\n",[11,1820,1821],{},"虽然我在本地开发时，先增加了两个必填字段desc、desc2，导致了当时数据被删除，后面又增加了desc、desc2、desc3三个可选字段，在正式环境的数据库中，数据并没有丢失，这三个可选字段也被加上了。为了验证修改也有数据的字段会不会产生影响，我把正式库和本地库的desc、desc2、desc3三个可选字段都造上数据，然后再去代码里把desc3改为desc4。",[11,1823,1824],{},"SQL如下",[22,1826,1828],{"className":133,"code":1827,"language":135,"meta":27,"style":27},"ALTER TABLE `color` DROP COLUMN `desc3`,\n    ADD COLUMN `desc4` VARCHAR(20) NULL;\n",[29,1829,1830,1846],{"__ignoreMap":27},[32,1831,1832,1834,1836,1838,1840,1842,1844],{"class":34,"line":35},[32,1833,1255],{"class":46},[32,1835,987],{"class":46},[32,1837,1260],{"class":42},[32,1839,1263],{"class":46},[32,1841,1266],{"class":46},[32,1843,1462],{"class":42},[32,1845,564],{"class":142},[32,1847,1848,1851,1853,1856,1858,1860,1862,1864,1866],{"class":34,"line":152},[32,1849,1850],{"class":46},"    ADD",[32,1852,1266],{"class":46},[32,1854,1855],{"class":42}," `desc4`",[32,1857,1044],{"class":38},[32,1859,397],{"class":142},[32,1861,1049],{"class":46},[32,1863,403],{"class":142},[32,1865,1054],{"class":46},[32,1867,993],{"class":142},[11,1869,1870],{},"然后再模拟一下部署上线，再次运行deploy。",[11,1872,1873],{},"然后在正式环境运行deploy没有问题。但在本地执行上边那一步时，会检查我的migration记录，导致我跑不起来，所以我在不知道如何操作的情况下，只能又从垃圾桶里把删除的文件还原。",[11,1875,1876],{},"然后把全部migrations部署到正式上去运行deploy时，也会提示我",[22,1878,1880],{"className":133,"code":1879,"language":135,"meta":27,"style":27},"A migration failed to apply. New migrations cannot be applied before the error is recovered from. Read more about how to resolve migration issues in a production database: https://pris.ly/d/migrate-resolve\n\nMigration name: 20240117075835_delete_table1\n",[29,1881,1882,1910,1914],{"__ignoreMap":27},[32,1883,1884,1887,1890,1892,1895,1898,1900,1903,1906],{"class":34,"line":35},[32,1885,1886],{"class":46},"A",[32,1888,1889],{"class":142}," migration failed to apply. New migrations cannot be applied before the error is recovered from. Read more about how to resolve migration issues ",[32,1891,863],{"class":155},[32,1893,1894],{"class":142}," a production ",[32,1896,1897],{"class":38},"database",[32,1899,558],{"class":142},[32,1901,1902],{"class":38},"https",[32,1904,1905],{"class":142},":",[32,1907,1909],{"class":1908},"sAwPA","//pris.ly/d/migrate-resolve\n",[32,1911,1912],{"class":34,"line":152},[32,1913,256],{"emptyLinePlaceholder":255},[32,1915,1916,1919,1922],{"class":34,"line":165},[32,1917,1918],{"class":142},"Migration ",[32,1920,1921],{"class":38},"name",[32,1923,1924],{"class":142},": 20240117075835_delete_table1\n",[11,1926,1927],{},"根据提示的地址，然后运行了，",[22,1929,1931],{"className":133,"code":1930,"language":135,"meta":27,"style":27},"/app # npx dotenv -e .prod.env -- prisma migrate resolve --rolled-back \"20240117075835_delete_table1\" \n",[29,1932,1933],{"__ignoreMap":27},[32,1934,1935,1937,1940,1942,1944,1946,1949,1951,1954,1956,1959],{"class":34,"line":35},[32,1936,1539],{"class":155},[32,1938,1939],{"class":142},"app # npx dotenv ",[32,1941,1504],{"class":155},[32,1943,1590],{"class":142},[32,1945,1380],{"class":155},[32,1947,1948],{"class":142}," prisma migrate resolve ",[32,1950,1380],{"class":155},[32,1952,1953],{"class":142},"rolled",[32,1955,1504],{"class":155},[32,1957,1958],{"class":142},"back ",[32,1960,1961],{"class":42},"\"20240117075835_delete_table1\"\n",[11,1963,1964],{},"提示如下。把20240117075835_delete_table1这条记录回滚了。",[22,1966,1968],{"className":133,"code":1967,"language":135,"meta":27,"style":27},"Migration 20240117075835_delete_table1 marked as rolled back.\n",[29,1969,1970],{"__ignoreMap":27},[32,1971,1972,1975,1977,1980,1983],{"class":34,"line":35},[32,1973,1974],{"class":142},"Migration 20240117075835_delete_table1 marked ",[32,1976,896],{"class":155},[32,1978,1979],{"class":38}," rolled",[32,1981,1982],{"class":38}," back",[32,1984,937],{"class":142},[11,1986,1987],{},"再次deploy时，因为20240117075835_delete_table1的sql里和正式数据库有冲突（重复创建了表），所以还是报错了。然后我就把20240117075835_delete_table1删了再重新部署上去deplpy，此时提示， 这个migrate在之前的一个时刻失败了。因为prisma有一个自己表，会记录整个迁移的过程。",[22,1989,1991],{"className":133,"code":1990,"language":135,"meta":27,"style":27},"The `20240117075835_delete_table1` migration started at 2024-01-17 09:13:27.542 UTC failed\n",[29,1992,1993],{"__ignoreMap":27},[32,1994,1995,1998,2001,2004,2007,2009,2012,2014,2017,2020,2022,2025,2027,2030,2033],{"class":34,"line":35},[32,1996,1997],{"class":142},"The ",[32,1999,2000],{"class":42},"`20240117075835_delete_table1`",[32,2002,2003],{"class":142}," migration started at ",[32,2005,2006],{"class":46},"2024",[32,2008,1504],{"class":155},[32,2010,2011],{"class":46},"01",[32,2013,1504],{"class":155},[32,2015,2016],{"class":46},"17",[32,2018,2019],{"class":46}," 09",[32,2021,1905],{"class":142},[32,2023,2024],{"class":46},"13",[32,2026,1905],{"class":142},[32,2028,2029],{"class":46},"27.542",[32,2031,2032],{"class":46}," UTC",[32,2034,2035],{"class":142}," failed\n",[11,2037,2038],{},"于是我又重新运行了一下resolve",[22,2040,2041],{"className":133,"code":1930,"language":135,"meta":27,"style":27},[29,2042,2043],{"__ignoreMap":27},[32,2044,2045,2047,2049,2051,2053,2055,2057,2059,2061,2063,2065],{"class":34,"line":35},[32,2046,1539],{"class":155},[32,2048,1939],{"class":142},[32,2050,1504],{"class":155},[32,2052,1590],{"class":142},[32,2054,1380],{"class":155},[32,2056,1948],{"class":142},[32,2058,1380],{"class":155},[32,2060,1953],{"class":142},[32,2062,1504],{"class":155},[32,2064,1958],{"class":142},[32,2066,1961],{"class":42},[11,2068,2069],{},"然后再次deploy，可以了。刷新了一下表里数据，desc3被删了，desc4也加上了。",[832,2071,2072],{"id":2072},"开始修复",[11,2074,2075],{},"但是由于一顿操作，搞的两边的prisma migrations不太同步了，具体哪里不一样我也忘了。而我只想跳过第一步会给我清空表数据再重新建表的migration。",[11,2077,2078,2079,2082,2083,2085,2086,2089,2090,2092,2093,2096],{},"于是我把本地",[29,2080,2081],{},"reset","一下，把",[29,2084,807],{},"全删掉，也不管他是啥状态了，先按照正确的迁移的做法建好一个",[29,2087,2088],{},"0_init","，标记为",[29,2091,1711],{},"，然后继续修改一些model字段，然后再使用",[29,2094,2095],{},"migrate dev","生成了一条migration记录。重新“部署上线”",[22,2098,2099],{"className":133,"code":1494,"language":135,"meta":27,"style":27},[29,2100,2101],{"__ignoreMap":27},[32,2102,2103,2105,2107,2109,2111,2113,2115],{"class":34,"line":35},[32,2104,1501],{"class":142},[32,2106,1504],{"class":155},[32,2108,1507],{"class":142},[32,2110,1380],{"class":155},[32,2112,1512],{"class":142},[32,2114,1504],{"class":155},[32,2116,1517],{"class":142},[11,2118,2119],{},"在docker内部尝试一下deploy",[22,2121,2123],{"className":24,"code":2122,"language":26,"meta":27,"style":27},"/app # pnpm migrate:deploy\n",[29,2124,2125],{"__ignoreMap":27},[32,2126,2127,2130],{"class":34,"line":35},[32,2128,2129],{"class":38},"/app",[32,2131,2132],{"class":1908}," # pnpm migrate:deploy\n",[11,2134,2135],{},"会报错：",[22,2137,2139],{"className":24,"code":2138,"language":26,"meta":27,"style":27},"A migration failed to apply. New migrations cannot be applied before the error is recovered from. Read more about how to resolve migration issues in a production database: https://pris.ly/d/migrate-resolve\n\nMigration name: 0_init\n\nDatabase error code: 1050\n",[29,2140,2141,2224,2228,2238,2242],{"__ignoreMap":27},[32,2142,2143,2145,2148,2151,2153,2156,2159,2162,2165,2168,2171,2174,2176,2179,2182,2185,2188,2191,2194,2197,2200,2202,2204,2206,2209,2212,2215,2218,2221],{"class":34,"line":35},[32,2144,1886],{"class":38},[32,2146,2147],{"class":42}," migration",[32,2149,2150],{"class":42}," failed",[32,2152,911],{"class":42},[32,2154,2155],{"class":42}," apply.",[32,2157,2158],{"class":42}," New",[32,2160,2161],{"class":42}," migrations",[32,2163,2164],{"class":42}," cannot",[32,2166,2167],{"class":42}," be",[32,2169,2170],{"class":42}," applied",[32,2172,2173],{"class":42}," before",[32,2175,899],{"class":42},[32,2177,2178],{"class":42}," error",[32,2180,2181],{"class":42}," is",[32,2183,2184],{"class":42}," recovered",[32,2186,2187],{"class":42}," from.",[32,2189,2190],{"class":42}," Read",[32,2192,2193],{"class":42}," more",[32,2195,2196],{"class":42}," about",[32,2198,2199],{"class":42}," how",[32,2201,911],{"class":42},[32,2203,778],{"class":42},[32,2205,2147],{"class":42},[32,2207,2208],{"class":42}," issues",[32,2210,2211],{"class":42}," in",[32,2213,2214],{"class":42}," a",[32,2216,2217],{"class":42}," production",[32,2219,2220],{"class":42}," database:",[32,2222,2223],{"class":42}," https://pris.ly/d/migrate-resolve\n",[32,2225,2226],{"class":34,"line":152},[32,2227,256],{"emptyLinePlaceholder":255},[32,2229,2230,2233,2236],{"class":34,"line":165},[32,2231,2232],{"class":38},"Migration",[32,2234,2235],{"class":42}," name:",[32,2237,784],{"class":42},[32,2239,2240],{"class":34,"line":174},[32,2241,256],{"emptyLinePlaceholder":255},[32,2243,2244,2247,2249,2252],{"class":34,"line":189},[32,2245,2246],{"class":38},"Database",[32,2248,2178],{"class":42},[32,2250,2251],{"class":42}," code:",[32,2253,2254],{"class":46}," 1050\n",[11,2256,2257],{},"这个错已经在本地看了无数遍了。好解决！把它标记为已应用",[22,2259,2261],{"className":24,"code":2260,"language":26,"meta":27,"style":27},"/app # npx dotenv -e .prod.env -- prisma migrate resolve --applied 0_init\n",[29,2262,2263],{"__ignoreMap":27},[32,2264,2265,2267],{"class":34,"line":35},[32,2266,2129],{"class":38},[32,2268,2269],{"class":1908}," # npx dotenv -e .prod.env -- prisma migrate resolve --applied 0_init\n",[11,2271,2272],{},"成功后再次使用deploy。可以看到没问题了，只执行了0_init之后的两个migration记录。",[15,2274,2275],{"id":2275},"附录",[2277,2278,2279,2287,2295],"ul",{},[464,2280,2281,2286],{},[433,2282,2285],{"href":2283,"rel":2284,"title":2285},"https://www.wolai.com/dZFPf36pn52aSaFT1TcvDr#qkBYymtEjsbxhg95MxLmn",[437],"Troubleshooting"," 包含了各种migration的操作",[464,2288,2289,2294],{},[433,2290,2292],{"href":435,"rel":2291,"title":438},[437],[440,2293,438],{},"  包含所有prisma cli 命令",[464,2296,2297,2301],{},[433,2298,2300],{"href":456,"rel":2299,"title":2300},[437],"Add to existing project"," 将prisma添加到现有项目中",[15,2303,2304],{"id":2304},"小结",[11,2306,2307],{},"以上就是从typeorm到prisma的变更过程及遇到的问题。",[11,2309,2310],{},"如果对你有帮助的话，希望可以点个关注支持一下 (๑•̀ㅂ•́)و✧",[15,2312,2313],{"id":2313},"相关文章",[11,2315,2316,2317],{},"Nest搭建： ",[433,2318,2321],{"href":2319,"rel":2320,"title":2321},"https://juejin.cn/post/7313418992310616101",[437],"一个产品要有一个“好底子”：Nest项目搭建",[11,2323,2324,2325],{},"Nest部署：",[433,2326,1491],{"href":1489,"rel":2327,"title":1491},[437],[2329,2330,2331],"style",{},"html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}",{"title":27,"searchDepth":152,"depth":152,"links":2333},[2334,2335,2336,2337,2338,2339],{"id":17,"depth":165,"text":17},{"id":450,"depth":165,"text":450},{"id":793,"depth":165,"text":794},{"id":2275,"depth":165,"text":2275},{"id":2304,"depth":165,"text":2304},{"id":2313,"depth":165,"text":2313},"2024-01-22T00:00:00.000Z","Prisma初体验","md",null,"2025-08-19T00:00:00.000Z",{"image":2343,"category":2346,"published":2347},"技术","2023-01-01","/post/nest/nest-from-typeorm-to-prisma","---\ntitle: Nest从TypeORM到Prisma：迁移记录\ndescription: Prisma初体验\nimage: \ntags: [\"Nest\", \"Prisma\"]\ncategory: 技术\nauthor: zzdaddy\npublished: 2023-01-01\ndate: 2024-01-22\nlastmod: 2025-08-19\nshowTitle: Nest从TypeORM到Prisma：迁移记录\n---\n最近把typeORM换成了prisma，主要是想用prisma的migrate功能，于是记录了一下其中发现的问题。\n\n### 安装\n\n全局安装prisma\n\n```shell\nnpm i -g prisma\n```\n\n项目安装@prisma/client\n\n```shell\npnpm add @prisma/client\n```\n\n在nest根目录初始化prisma，初始化后会在根目录创建一个prisma目录以及一个`.env`，目录里面有个`schema.prisma` 。\n\n```shell\nnpx prisma init\n```\n\nnest创建一个新的模块prisma\n\n```shell\nnest g resource prisma\n```\n\n可以用`@Global`把这个模块注册成全局模块。\n\n`service部分`可以参考nest官网的推荐或者其他野文，基本上都一样\n\n```typescript\n@Injectable()\nexport class PrismaService\n  extends PrismaClient\n  implements OnApplicationBootstrap, OnApplicationShutdown\n{\n  constructor() {\n    super();\n  }\n  async onApplicationBootstrap() {\n    await this.$connect();\n  }\n\n  async onApplicationShutdown() {\n    await this.$disconnect();\n  }\n}\n```\n\n把之前typeORM相关的，find、save等等方法一一替换成prisma。比如：\n\n```typescript\nasync findUserById(id: bigint) {\n    return this.prisma.user.findUnique({\n      where: {\n        id,\n      },\n    });\n  } \n```\n\n确定没有问题之后，看一下prisma的常用命令。这部分也可以先跳过，migrate dev命令放在了正确的迁移这一小节中。\n\n`db pull `\n基于数据库schema生成prisma schema\n\n`prisma generate`\n生成client代码，不会同步数据库\n\n`prisma db push`\n用于将本地的表结构改动同步到数据库。\n\n如果表里已经有了字段和数据，再新增字段时，务必设置为允许为null的可选字段，如`String?`，否则会清空表结构。虽然会提示你，也不排除很多人会不看英文提示。\n\n```shell\n username   String?  @db.VarChar(255) ✅\n username2  String  @db.VarChar(255)  ❌\n```\n\n如果是删除了字段。运行后会同步到mysql数据库中。同时不会删除已有的数据，只会删除对应的字段。\n\n[**Prisma CLI reference**](https://www.prisma.io/docs/orm/reference/prisma-cli-reference#migrate-resolve \"Prisma CLI reference\") 👈此处有所有命令，还是建议看看官方文档。\n\n**注意此操作不会像migrate一样会被记录**\n\n### 正确的迁移\n\n迁移过程也有[官方文档](https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project \"官方文档\")👈，这里相当于我实践后又讲述了一遍。\n\n1.  首先设置好开头`init`好的`schema.prisma`，如：\n\n```typescript\ndatasource db {\n  provider = \"mysql\"\n  url      = env(\"DATABASE_URL\")\n}\n```\n\n1.  使用dotenv重新配置一下env文件，和项目现有的env文件结合起来，如：\n\n```shell\nnpm install -g dotenv-cli\n```\n\n在`package.json`配置一下常用命令\n\n```typescript\n  \"migrate:dev\": \"npx dotenv -e .dev.env -- prisma migrate dev\",\n    \"migrate:deploy\": \"npx dotenv -e .prod.env -- prisma migrate deploy\",\n    \"prisma:generate:dev\": \"npx dotenv -e .dev.env -- prisma generate\",\n    \"prisma:generate:prod\": \"npx dotenv -e .prod.env -- prisma generate\",\n    \"db:push:dev\": \"npx dotenv -e .dev.env -- prisma db push\",\n    \"db:push:prod\": \"npx dotenv -e .prod.env -- prisma db push\",\n    \"db:pull:dev\": \"npx dotenv -e .dev.env -- prisma db pull\",\n    \"db:pull:prod\": \"npx dotenv -e .prod.env -- prisma db pull\",\n    \"db:seed:dev\": \"npx dotenv -e .dev.env -- prisma db seed\",\n    \"db:seed:prod\": \"npx dotenv -e .prod.env -- prisma db seed\",\n```\n\n然后把prisma默认的env里配置的`DATABASE_URL`挪到`.dev.env` 和` .prod.env`里去，在不同环境测试一下能否正确连接数据库。\n\n由于数据库里已有表结构和数据，prisma属于后来者，我不想丢失数据和结构，所以先使用db pull 生成prisma schema\n\n```shell\npnpm db:pull:dev\n```\n\n最关键的一点来了。migrate dev会记录迁移的过程，如果直接使用migrate dev的话，他会清空数据，并生成一个migration和建表的sql，这样显然不符合预期。所以先手动创建一个migration\n\n```shell\nmkdir -p prisma/migrations/0_init\n```\n\n然后使用prisma migrate diff 输出一个sql脚本\n\n```shell\nnpx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script > prisma/migrations/0_init/migration.sql\n```\n\n此时，检查一下生成sql语句确保没有任何问题，然后把这个migration标记为已应用\n\n```shell\nnpx prisma migrate resolve --applied 0_init\n```\n\n这个时候，去修改prisma schema后再使用migrate dev，就会生成基于已有数据库后的一个变更了。这个变更就不会影响已有数据了。\n\n下边我也基于migrate dev的操作做了一些场景重现，感兴趣的可以瞅瞅。\n\n### migrate dev 操作演示\n\n假设我不懂如何在两个环境之间迁移，只是在本地把玩prisma，直到我开始想到和线上数据库要同步一下。\n\n运行`pnpm migrate:dev`会在prisma目录下生成一个`migrations`目录，并生成本次操作的sql。\n\n现在本地数据库已经有了一堆表，刚刚接入了prisma，或者本地乱搞的时候已经不知道是什么状态了。我通过`init`后用`db puill `生成了`prisma schame` 然后我在`prisma schema`中删除了一个`model`。\n\n#### 删除表\n\n然后运行`pnpm migrate:dev` 并在提示下命名为`delete_table1`。它会提示你的数据库架构与迁移历史不一致，然后继续执行。因为此时我本地的状态已经混乱，migrations记录已经不在了，所以他从头开始给我记录了。\n\n```typescript\nDrift detected: Your database schema is not in sync with your migration history.\nThe following is a summary of the differences between the expected database schema given your migrations files, and the actual schema of the database.\nIt should be understood as the set of changes to get from the expected schema to the actual schema.\n\n```\n\n此时可以看到目录下生成对应的sql，**里面是一堆创建表的SQL语句**。再去本地数据库看的时候，数据都被清除了，并且自动执行了`seed.ts` 插入了假数据\n\n![](https://img.zzao.club/article/202411191448363.png)\n\n然后再删除一张表`admin_user`，然后再运行一下`migrate:dev`，这次命名为`delete_table2`，可以看到这次没有提示错误，只是单纯的删除了一张表\n\n![](https://img.zzao.club/article/202411191448364.png)\n\n```typescript\nDROP TABLE `admin_user`;\n```\n\n#### 增加表\n\n这个没有什么可说的\n\n```typescript\nCREATE TABLE `color2` (\n    `id` INTEGER NOT NULL AUTO_INCREMENT,\n    `desc` VARCHAR(20) NULL,\n    `desc2` VARCHAR(20) NULL,\n    `desc3` VARCHAR(20) NULL,\n    `value` VARCHAR(255) NOT NULL,\n    `createTime` DATETIME(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),\n    `updateTime` DATETIME(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),\n    `userId` BIGINT NOT NULL,\n\n    PRIMARY KEY (`id`)\n) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n```\n\n#### 删除字段\n\n然后我找了一张表`color`，给他直接加了一条数据，然后在`model`里把其中一个字段`desc`删掉，再去运行`pnpm migrate:dev`，这次生成的SQL里也只是删除了一个字段，对数据没有影响。\n\n```typescript\nALTER TABLE `color` DROP COLUMN `desc`;\n```\n\n#### 增加字段\n\n还是这张表`color`，再加两个必填字段`desc`、`desc2`\n\n```typescript\ndesc       String   @db.VarChar(20)\ndesc2      String   @db.VarChar(20)\n```\n\n因为这俩字段都是必填的，所以直接把表清空了，这次也没提示会清空表（？）。\n\n再去数据造一条数据，然后再把这俩字段改成可选，再次执行`pnpm migrate:dev`\n\n```typescript\ndesc       String?   @db.VarChar(20)\ndesc2      String?   @db.VarChar(20)\n```\n\n这次没有数据没有被清，只是字段被设置了可以为null\n\n```typescript\n-- AlterTable\nALTER TABLE `color` MODIFY `desc` VARCHAR(20) NULL,\n    MODIFY `desc2` VARCHAR(20) NULL;\n\n```\n\n直接添加一个可选字段`desc3`，这次只是单纯增加了一个可以为null的desc3字段\n\n```typescript\n-- AlterTable\nALTER TABLE `color` ADD COLUMN `desc3` VARCHAR(20) NULL;\n```\n\n正常情况下，已经有数据的表，不可能再增加一个必填字段了，因为已有字段就都不符合条件了。我这里只是演示一下，对于一个没接触过后端和数据库的前端来说，这个可能也是需要考虑的......\n\n#### 同步到其他环境的数据库\n\n如果本地有docker容器的端口会冲突的话，先stop一下，然后把nest项目本地用docker-compose跑起来。\n\n如何使用docker-compose 部署nest项目可以看下我这篇文章，里面有详细解释 👉[使用 Docker Compose 部署 Nest 应用](https://juejin.cn/post/7316202589603807259 \"使用 Docker Compose 部署 Nest 应用\")。\n\n```typescript\ndocker-compose up --build -d\n```\n\n打包完成后，来到nest服务内部，这里我用的Docker Desktop 演示。此时，正式数据库还是没有`color2`这个表，color表里也没有`desc2` 和`desc`的。然后运行一下，为了保证他运行不报错，我先把20240117080859\\_delete\\_table1删了，因为这个sql里全是建表的语句。后面会重新来一下这个过程来弥补这个错误。\n\n```typescript\n/app # pnpm migrate:deploy\n\n> master@0.0.1 migrate:deploy /app\n> npx dotenv -e .prod.env -- prisma migrate deploy\n\nPrisma schema loaded from prisma/schema.prisma\nDatasource \"db\": MySQL database \"zzstudio\" at \"zz_mysql:3306\"\n\n6 migrations found in prisma/migrations\n\nApplying migration `20240117080859_delete_table2`\nApplying migration `20240117082030_delete_field1`\nApplying migration `20240117082351_add_desc2`\nApplying migration `20240117082752_optional1`\nApplying migration `20240117083056_add_desc3`\nApplying migration `20240117083359_addtablecolor2`\n\nThe following migrations have been applied:\n\nmigrations/\n  └─ 20240117080859_delete_table2/\n    └─ migration.sql\n  └─ 20240117082030_delete_field1/\n    └─ migration.sql\n  └─ 20240117082351_add_desc2/\n    └─ migration.sql\n  └─ 20240117082752_optional1/\n    └─ migration.sql\n  └─ 20240117083056_add_desc3/\n    └─ migration.sql\n  └─ 20240117083359_addtablecolor2/\n    └─ migration.sql\n      \nAll migrations have been successfully applied.\n\n```\n\n虽然我在本地开发时，先增加了两个必填字段desc、desc2，导致了当时数据被删除，后面又增加了desc、desc2、desc3三个可选字段，在正式环境的数据库中，数据并没有丢失，这三个可选字段也被加上了。为了验证修改也有数据的字段会不会产生影响，我把正式库和本地库的desc、desc2、desc3三个可选字段都造上数据，然后再去代码里把desc3改为desc4。\n\nSQL如下\n\n```typescript\nALTER TABLE `color` DROP COLUMN `desc3`,\n    ADD COLUMN `desc4` VARCHAR(20) NULL;\n```\n\n然后再模拟一下部署上线，再次运行deploy。\n\n然后在正式环境运行deploy没有问题。但在本地执行上边那一步时，会检查我的migration记录，导致我跑不起来，所以我在不知道如何操作的情况下，只能又从垃圾桶里把删除的文件还原。\n\n然后把全部migrations部署到正式上去运行deploy时，也会提示我\n\n```typescript\nA migration failed to apply. New migrations cannot be applied before the error is recovered from. Read more about how to resolve migration issues in a production database: https://pris.ly/d/migrate-resolve\n\nMigration name: 20240117075835_delete_table1\n```\n\n根据提示的地址，然后运行了，\n\n```typescript\n/app # npx dotenv -e .prod.env -- prisma migrate resolve --rolled-back \"20240117075835_delete_table1\" \n```\n\n提示如下。把20240117075835\\_delete\\_table1这条记录回滚了。\n\n```typescript\nMigration 20240117075835_delete_table1 marked as rolled back.\n```\n\n再次deploy时，因为20240117075835\\_delete\\_table1的sql里和正式数据库有冲突（重复创建了表），所以还是报错了。然后我就把20240117075835\\_delete\\_table1删了再重新部署上去deplpy，此时提示， 这个migrate在之前的一个时刻失败了。因为prisma有一个自己表，会记录整个迁移的过程。\n\n```typescript\nThe `20240117075835_delete_table1` migration started at 2024-01-17 09:13:27.542 UTC failed\n```\n\n于是我又重新运行了一下resolve\n\n```typescript\n/app # npx dotenv -e .prod.env -- prisma migrate resolve --rolled-back \"20240117075835_delete_table1\" \n```\n\n然后再次deploy，可以了。刷新了一下表里数据，desc3被删了，desc4也加上了。\n\n#### 开始修复\n\n但是由于一顿操作，搞的两边的prisma migrations不太同步了，具体哪里不一样我也忘了。而我只想跳过第一步会给我清空表数据再重新建表的migration。\n\n于是我把本地`reset`一下，把`migrations`全删掉，也不管他是啥状态了，先按照正确的迁移的做法建好一个`0_init`，标记为`applied`，然后继续修改一些model字段，然后再使用`migrate dev`生成了一条migration记录。重新“部署上线”\n\n```typescript\ndocker-compose up --build -d\n```\n\n在docker内部尝试一下deploy\n\n```shell\n/app # pnpm migrate:deploy\n```\n\n会报错：\n\n```shell\nA migration failed to apply. New migrations cannot be applied before the error is recovered from. Read more about how to resolve migration issues in a production database: https://pris.ly/d/migrate-resolve\n\nMigration name: 0_init\n\nDatabase error code: 1050\n```\n\n这个错已经在本地看了无数遍了。好解决！把它标记为已应用\n\n```shell\n/app # npx dotenv -e .prod.env -- prisma migrate resolve --applied 0_init\n```\n\n成功后再次使用deploy。可以看到没问题了，只执行了0\\_init之后的两个migration记录。\n\n### 附录\n\n-   [Troubleshooting](https://www.wolai.com/dZFPf36pn52aSaFT1TcvDr#qkBYymtEjsbxhg95MxLmn \"Troubleshooting\") 包含了各种migration的操作\n-   [**Prisma CLI reference**](https://www.prisma.io/docs/orm/reference/prisma-cli-reference#migrate-resolve \"Prisma CLI reference\")  包含所有prisma cli 命令\n-   [Add to existing project](https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project \"Add to existing project\") 将prisma添加到现有项目中\n\n### 小结\n\n以上就是从typeorm到prisma的变更过程及遇到的问题。\n\n如果对你有帮助的话，希望可以点个关注支持一下 (๑•̀ㅂ•́)و✧\n\n### 相关文章\n\nNest搭建： [一个产品要有一个“好底子”：Nest项目搭建](https://juejin.cn/post/7313418992310616101 \"一个产品要有一个“好底子”：Nest项目搭建\")\n\nNest部署：[使用 Docker Compose 部署 Nest 应用](https://juejin.cn/post/7316202589603807259 \"使用 Docker Compose 部署 Nest 应用\")\n",{"title":5,"description":2341},"post/Nest/nest-from-typeorm-to-prisma",[2353,2354],"Nest","Prisma","yi54lOMVJmDSOv937sE9gK9UPn0L76wIZns6Y-_Dt6o",[2357,2361],{"title":2358,"path":2359,"stem":2360},"OpenClaw 安装入门（Windows）","/post/zzao/openclaw/openclaw-install-windows","post/zzao/openclaw/openclaw-install-windows",{"title":2362,"path":2363,"stem":2364},"假设你是AI，你的Skill应该是什么样的","/post/zzao/ai-skill-structure","post/zzao/ai-skill-structure",1779005086941]