---
# System prepended metadata

title: 小組學習筆記_淺談Express_基本MVC架構和ORM工具介紹
tags: ["Node.js\_"]

---

# 小組學習筆記_淺談Express_基本MVC架構和ORM工具介紹
撰寫人: 組長 許哲誠/terry xu
# 介紹MVC架構:

*   Model :
    *   定義資料庫料結構和利用CRUD的方式和資料庫進行溝通 ( 有使用ORM工具的情況。
    *   撰寫SQL語法並與資料庫進行溝通 ( 使用SQL模組。
*   Controller
    *   Controller 在過程中扮演 Model 和 View 中間的協調者，當不同路由（route）接收到 request 時，會呼叫 Controller 執行相對應的 Method。
*   View
    *   負責處理畫面的部分（template），也就是我們看到的網頁內容( 就是上課使用到ejs的部分
    *   由於我們是用React，所以就沒有view的部分。

# 介紹ORM工具

*   大專打算用prisma，但市面上也很多種ORM工具，個人只用過Sequelize，但Prisma看起來強一點。
*   ORM 在網站開發的架構中，通常是在 **資料庫** 和 **Model** 兩者之間，簡單來說，他就是讓使用者用更簡便、安全去操作資料庫。

## ORM工具優缺點

*   優點
    *   安全性 : 
        *   ORM工具通常會自動處理SQL注入問題,因為使用參數化查詢。
        *   ORM工具會過濾掉可能有害的SQL語句,如未經授權的DELETE操作。
    *   提高了可讀性和可維護性 : ORM允許開發者使用面向對象的方式與資料庫交流,無需直接編寫SQL語句。
    *   生產力提升 : 現在很多ORM工具都有提供許多現成的功能,如緩存、延遲加載等。
    *   版本控制 : 比較好管理資料庫結構的變更。
*   缺點
    *   效能問題 : 畢竟他在後面有做轉換成SQL語句等等，所以多多少少會比直接用SQL語句來的慢很多。
    *   複雜查詢 : 對於非常複雜的查詢,使用ORM可能會變得繁瑣,有時直接編寫SQL會更高效和清晰，例如有用到跨好幾個表查詢時。
    *   容易忘本 : 畢竟ORM都幫你做完了，反而SQL語句忘了就不太OK了。
    *   現在國外感覺都比較推崇直接寫SQL，畢竟速度比較快，但我覺得可以視情況而定，像是購物網站這種會大量CRUD需求的的，也許就不太適合ORM。

## ORM工具 VS SQL語法

時空背景 : 假設我們有一個 'Product' 模型,包含 id, name, price 字段。

### ORM建構 VS SQL 建構
```javascript
    datasource db {
      provider = "mysql"  // MariaDB 使用 MySQL
      url      = env("DATABASE_URL")
     //記得所有重要的apikey 是帳號都要寫在.env檔喔
      //甚麼是env檔，是用來存儲環境變量，很適合拿來放api key之類的個人隱私資料，
    }
    // 定義 Prisma 客戶端生成器
    generator client {
      provider = "prisma-client-js"
    }
    // 定義 Product 模型
    model Product {
      id    Int     @id @default(autoincrement())
      name  String
      price Decimal @db.Decimal(10, 2)
    }

    // 用於原生 SQL MariaDB 客戶端
    const mariadb = require('mariadb')
    const pool = mariadb.createPool({
      host: process.env.DB_HOST,//記得所有重要的api key 或是 帳號都要寫在.env檔喔
      user: process.env.DB_USER,
      password: process.env.DB_PASSWORD,
      database: process.env.DB_NAME,
      connectionLimit: 5
    })
```
### Prisma create VS SQL create
```javascript
    // 使用 Prisma create產品
    async function createProductPrisma(name, price) {
      const product = await prisma.product.create({//記得要寫async await用法喔
        data: { name, price },
      })
    }
    //為什麼要async await 這個坑之後再來說~~

    //用SQL create 產品
    async function createProductSQL(name, price) {
      let conn;
      try {
        conn = await pool.getConnection()
        const result = await conn.query(
          'INSERT INTO Product (name, price) VALUES (?, ?)',
          [name, price]
        )
      } finally {
        if (conn) conn.release()
    }
    //Database Connection Pool 這是個用來維護的資料庫連接的快取，
    //這個可學可不學，個人覺得還好QWQ
    //想學可以來找Terry。
```
### Prisma read VS SQL read
```javascript
    // 使用 Prisma 讀取產品
    async function getProductPrisma(id) {
      const product = await prisma.product.findUnique({//有很多種用法
        where: { id: id },

    }
    //findUnique查找特定條件的單個記錄
    //findMany查找多個記錄
    //有可以配合很多酷酷的用法，ex. where select include 等等等

    // 使用原生 SQL 讀取產品
    async function getProductSQL(id) {
      let conn;
      try {
        conn = await pool.getConnection()
        const rows = await conn.query('SELECT * FROM Product WHERE id = ?', [id])
      } finally {
        if (conn) conn.release()
      }
    }
```
其他作者懶得打了，自已去看酷酷的官方文件🦥。
https://www.prisma.io/docs

# 大專前後端分離

![express_MVC](https://hackmd.io/_uploads/Hk9IgiPOR.svg)

流程如下 :

1.  View ( React ) 會利用Client ( axios )** 發送一個request給到Route。
2.  Route透過使用者輸入特定路徑時要指定呼叫 controller 檔案中哪個指定的函式。
3.  Controller呼叫Model去資料庫做CRUD。
4.  Controller拿到資料後再response回去Client端。
5.  Client ( axios ) 端再把拿到的資料 ( JSON ) 做完處理後傳給View (React)。

*   axios會再生一份簡易教材出來~~

## 後端方面的範例程式

( 用MVC架構寫的，所以裏頭的view請自動忽略它 )

### App.JS (主要用來掛載路由跟啟動酷酷的express
```javascript
    // 掛上todo 路由
    const express = require('express');
    const bodyParser = require('body-parser');
    const todoRoutes = require('./routes/todoRoutes'); // routes路由import
    const app = express();
    const port = 8080;
    app.use('/todo', todoRoutes);
    app.use('/member', mRoutes);
    // 根路由重定向到 /todo
    app.get('/', (req, res) => res.redirect('/todo'));
    app.listen(port, () => {
      console.log(`Server is running on port ${port}`);
    });
```
routes.js

❗❗❗❗❗請記得寫成restful API ( 不熟的人請洽組長
```javascript
    const express = require('express');
    const router = express.Router();
    const {todoController} = require('../controllers/todoController');

    // get所有 todos
    router.get('/', todoController.showTodos);
    // post一個新的 todo
    router.post('/', todoController.createTodo);
    // get一個特定的 todo 以便編輯
    router.get('/:id/edit', todoController.editTodo);
    // update一個特定的 todo
    router.put('/:id', todoController.updateTodo);
    // delete一個特定的 todo
    router.delete('/:id', todoController.deleteTodo);

    module.exports = router;//要記得export出去喔
```
Controller.js
```javascript
    const express = require('express');
    const {todoModel}= require('../models/todo');//把model require近來
    const e = require('express');
    const router = express.Router();

    const todoController = {
      showTodos:async (req, res) => {//記得要寫async await !!!!!
        var todoItem = await todoModel.read();//這裡去呼叫model裡的read用法
        todoItem=todoItem.map(item=>item.dataValues);;
        res.render('index', {todoItem: todoItem});
        //這裡由於本來是寫成MVC架構，這裡傳給EJS，正常這裡是要response回去Client端
      },
    //....後面方法我懶得放了(好多東西
    };
    exports.todoController = todoController;//要記得export出去喔
```
Model.js
```javascript
    const { PrismaClient } = require('@prisma/client');//Prisma模型定義~~~
    const prisma = new PrismaClient()

    const todoModel = {//定義CRUD的用法~~~
    //請記得用CRUD的用法，不會的話請洽Terry
      create: async (title, isComplete) => {
        return await prisma.todoitem.create({
        });
      },
      read: async () => {
        return await prisma.todoitem.findMany();
      },
      update: async (todoTableId, title, isComplete) => {
        return await prisma.todoitem.update({

        });
      },
      delete: async (todoTableId) => {
        return await prisma.todoitem.delete({,
        });
      },
    };

    exports.todoModel = todoModel;//要記得export出去喔
```
# 結語
以上資訊為TerryXu的經驗，有寫錯誤的話請不吝嗇找我討論。

還沒補齊的知識點 :
*   async await ( 🌟🌟🌟
*   Prisma ( 🌟🌟
*   axios (🌟🌟
*   CRUD ( 🌟🌟