Closed8
prismaを使って既存のテーブルからscalikejdbc用のmapperクラスを生成する

prismaのversion確認用のpackage.json
{
"name": "prisma-generate-scala",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"@prisma/generator-helper": "^6.3.0",
"prisma": "^6.3.0"
}
}

適当に作ったecサイト用っぽいsql
CREATE TABLE items (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
price DECIMAL(10, 2) NOT NULL,
stock INT DEFAULT 0
);
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL
);
CREATE TABLE current_cart (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL UNIQUE,
item_id INT NOT NULL,
quantity INT NOT NULL DEFAULT 1,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (item_id) REFERENCES items(id)
);
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
total_amount DECIMAL(10, 2) NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE TABLE ordered_items (
id INT AUTO_INCREMENT PRIMARY KEY,
order_id INT NOT NULL,
item_id INT NOT NULL,
quantity INT NOT NULL,
price_at_purchase DECIMAL(10, 2) NOT NULL,
FOREIGN KEY (order_id) REFERENCES orders(id),
FOREIGN KEY (item_id) REFERENCES items(id)
);

元のprisma/schema.prisma
generator scala {
provider = "node ./prisma/scala-generator.js"
output = "../dist"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
.env(ローカル用なのでメモがてら載せる
DATABASE_URL="mysql://root:rootpassword@127.0.0.1:3306/ecsite-samples"

prisma db pull後のprisma/schema.prisma
generator scala {
provider = "node ./prisma/scala-generator.js"
output = "../dist"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model current_cart {
id Int @id @default(autoincrement())
user_id Int @unique(map: "user_id")
item_id Int
quantity Int @default(1)
users users @relation(fields: [user_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "current_cart_ibfk_1")
items items @relation(fields: [item_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "current_cart_ibfk_2")
@@index([item_id], map: "item_id")
}
model items {
id Int @id @default(autoincrement())
name String @db.VarChar(255)
description String? @db.Text
price Decimal @db.Decimal(10, 2)
stock Int? @default(0)
current_cart current_cart[]
ordered_items ordered_items[]
}
model ordered_items {
id Int @id @default(autoincrement())
order_id Int
item_id Int
quantity Int
price_at_purchase Decimal @db.Decimal(10, 2)
orders orders @relation(fields: [order_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "ordered_items_ibfk_1")
items items @relation(fields: [item_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "ordered_items_ibfk_2")
@@index([item_id], map: "item_id")
@@index([order_id], map: "order_id")
}
model orders {
id Int @id @default(autoincrement())
user_id Int
order_date DateTime? @default(now()) @db.Timestamp(0)
total_amount Decimal @db.Decimal(10, 2)
ordered_items ordered_items[]
users users @relation(fields: [user_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "orders_ibfk_1")
@@index([user_id], map: "user_id")
}
model users {
id Int @id @default(autoincrement())
username String @unique(map: "username") @db.VarChar(255)
email String @unique(map: "email") @db.VarChar(255)
password_hash String @db.VarChar(255)
current_cart current_cart?
orders orders[]
}

scala-generator.js
#!/usr/bin/env node
const { writeFileSync, mkdirSync } = require("fs");
const path = require("path");
const { generatorHandler } = require("@prisma/generator-helper");
generatorHandler({
onManifest: () => ({}),
onGenerate: async (options) => {
console.log("Prisma Custom Generator: Running...");
const outputDir = options.generator.output.value;
mkdirSync(outputDir, { recursive: true });
for (const model of options.dmmf.datamodel.models) {
const className = toPascalCase(model.name);
const fileName = className + ".scala";
const fields = model.fields.map((field) => {
return ` ${field.name}: ${
mapPrismaTypeToScala(field.type, field.isRequired)
}`;
}).join(",\n");
const scalaClass = `
package com.example
case class ${className}(
${fields}
)`;
const filePath = path.join(outputDir, fileName);
writeFileSync(filePath, scalaClass);
console.log(`Generated: ${filePath}`);
}
},
});
function mapPrismaTypeToScala(prismaType, isRequired) {
const typeMapping = {
Int: "Int",
String: "String",
Boolean: "Boolean",
DateTime: "java.time.LocalDateTime",
Float: "Double",
Decimal: "BigDecimal",
};
const scalaType = typeMapping[prismaType] || "String";
return isRequired ? scalaType : `Option[${scalaType}]`;
}
function toPascalCase(str) {
return str
.replace(/[_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "")
.replace(/^./, (m) => m.toUpperCase());
}

prisma generateで生成されるScalaコードの一部
dist/Item.scala
package com.example
case class Items(
id: Int,
name: String,
description: Option[String],
price: BigDecimal,
stock: Option[Int],
current_cart: String,
ordered_items: String
)

あーpropertyがsnake caseになってるわ

まとめた
このスクラップは2ヶ月前にクローズされました