lwliu
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- tags: Java, Database, Spring Boot --- # JDBC & JDBC TEMPLATE 介紹 ## 前言 資料庫操作是開發程式中不可或缺的一環,在 Java 中我們經常使用 JDBC 以及延伸的 JDBC Template,本篇將透過情境模擬來學習如何建置環境與實際應用。 ## 目錄 * [JDBC & JDBC TEMPLATE 介紹](#JDBC-&-JDBC-TEMPLATE-介紹) * [前言](#前言) * [目錄](#目錄) * [介紹/基本概念](#介紹基本概念) * [JDBC](#JDBC) * [JDBC Template](#JDBC-Template) * [DAO](#DAO) * [Driver](#Driver) * [常見的關鍵詞](#常見的關鍵詞) * [應用情境說明](#應用情境說明) * [建置環境](#建置環境) * [MariaDB + HeidiSQL 安裝](#MariaDB-+-HeidiSQL-安裝) * [DB 準備與建立專案](#DB-準備與建立專案) * [實作過程](#實作過程) * [JDBC - 建立 Java 專案](#JDBC---建立-Java-專案) * [JDBC Template - 建立 Spring Boot 專案](#JDBC-Template---建立-Spring-Boot-專案) * [參考資料](#參考資料) * [撰寫紀錄](#撰寫紀錄) ## 介紹/基本概念 ### JDBC 全名為 *Java Database Connectivity*,是 Java 提供客戶端連接資料庫的 API,根據不同廠商的資料庫只需要更換對應的 Driver 就可以成功連接。 ### JDBC Template Spring 封裝加工後的一個 class,使用上大同小異但是方便許多,也避免掉一些撰寫 JDBC 容易出錯的地方。 ![1](https://i.imgur.com/KQtizoV.png) ### DAO 全名為 *Data Access Object*(資料存取物件),實作 DAO 能夠將資料庫存取與業務邏輯分離,操作資料庫只需要提供參數,無須理解背後運作。 ### Driver 通常為資料庫廠商提供,負責告知 Spring Boot 該如何設定與操作資料庫。 ### 常見的關鍵詞 * DriverManager:負責載入 Driver 與連接資料庫的動作 * Connection:負責與資料庫溝通,所有對資料庫的操作要藉此實現 * Statement:用於執行 SQL 操作 * PreparedStatement:用於執行 SQL 操作(參數化,防止 SQL Injection) * ResultSet:用於存放查詢結果 * SQLException:SQL 操作上發生了例外狀況 ## 應用情境說明 JDBC 為 Java 中最基本的資料庫操作方式,JDBC Template 雖然較麻煩但是能夠做到很好的權責分離。 ## 建置環境 ### MariaDB + HeidiSQL 安裝 * Step 1 - [安裝 MariaDB :link:](https://mariadb.org/download/) :warning: 設立 root 帳號的密碼 ![1](https://i.imgur.com/ICJkJrG.png) :warning: 確認 port 是否有阻擋 (預設 3306) ![2](https://i.imgur.com/F5QMXG7.png) * Step 2 - [安裝 HeidiSQL :link:](https://www.heidisql.com/download.php) * Step 3 - 測試 HeidiSQL 連線 ![3](https://i.imgur.com/BhECcGV.png) ### DB 準備與建立專案 請複製這段 table 的建立指令 ```sql= CREATE TABLE `mytable` ( `name` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8_general_ci', `id` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8_general_ci' ) COLLATE='utf8_general_ci'mytable3 ENGINE=InnoDB ; ``` 開啟 HeidiSQL,在 MySQL 的查詢頁籤上執行這段程式碼 ![1](https://i.imgur.com/PUELZt7.png) ## 實作過程 ### JDBC - 建立 Java 專案 專案結構如下 ![1](https://i.imgur.com/m5dP1az.png) 建立一個 package 名稱是 jdbc > JDBCDemo.java ![2](https://i.imgur.com/XLtKWeX.png) 建立完成後選擇 Driver 載入方式 1. 無 Maven -> 下載 Driver 並匯入專案 library | Database | Driver Name | | -------- |:-------------------------------------------------- | | MySQL | [Connector for MySQL :link:][DolphinDL] | | MariaDB | [Connector for MariaDB :link:][MariaDL] | | MSSQL | [Microsoft JDBC Driver for SQL Server :link:][MSDL]| [DolphinDL]: https://dev.mysql.com/downloads/connector/j/ [MariaDL]: https://mariadb.com/downloads/#connectors [MSDL]: https://docs.microsoft.com/zh-tw/sql/connect/jdbc/download-microsoft-jdbc-driver-for-sql-server?view=sql-server-ver15 放入專案同階層的資料夾底下 ![3](https://i.imgur.com/QyjwJk6.png) File > Project Structure > Libraries > + > Java > 選擇 jar ![4](https://i.imgur.com/u5YCGh2.png) ![5](https://i.imgur.com/OgXJObj.jpg) 2.有 Maven -> [透過 Maven 自動下載 :link:](https://mvnrepository.com/artifact/mysql/mysql-connector-java/8.0.22) ![1](https://i.imgur.com/juucIG7.png) * pom.xml ```xml= <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.22</version> </dependency> </dependencies> ``` #### JDBC 部分 ##### 前置動作 : 宣告 Connection 物件並使用 DriverManager 建立連線 ```java Connection conn = null; // 使用 Class.forName() 載入 Driver 給 DriverManager try { Class.forName("com.mysql.cj.jdbc.Driver"); System.out.println("Driver loaded!"); } catch (ClassNotFoundException e) { System.out.println("找不到驅動程式類別"); e.printStackTrace(); } // 再使用 DriverManager.getConnection() 取得有效的 Connection 物件 // jdbc 格式 : jdbc:DB 廠商名/辨識字詞:URL try { conn = DriverManager.getConnection ( "jdbc:mysql://localhost:3306/mysql?serverTimezone=UTC", // URL "root", // 用户名 "********"// 密碼 ); } catch (SQLException e) { System.out.println("連線失敗"); e.printStackTrace(); } ``` ##### 實作 : 使用 Statement & PreparedStatement ```java= // 建立 Statement 物件 Statement stmt = conn.createStatement(); ``` 新刪查改範例 ```java= // 新增一筆資料 stmt.executeUpdate("INSERT INTO MyTable( name ) VALUES ( 'my name' ) "); // 修改一筆資料 stmt.executeUpdate("UPDATE MyTable SET name = 'Another name' WHERE name = 'my name' "); // 刪除一筆資料 stmt.executeUpdate("DELETE FROM MyTable WHERE name = 'Another name' "); // 查詢一筆資料 ResultSet rs = stmt.executeQuery("SELECT * FROM MyTable"); // 確認結果集是否為空 while (rs.next()) { // 取得一筆資料的欄位數量 int numColumns = rs.getMetaData().getColumnCount(); // 使用迴圈將資料印出 for (int i = 1; i <= numColumns; i++) { System.out.println("COLUMN " + i + " = " + rs.getObject(i)); } } ``` 使用後關閉物件 ```java rs.close(); stmt.close(); ``` PrepareStatement ```java // 建立 PreparedStatement 物件 PreparedStatement ps = null; ``` 新刪查改範例 ```java // 新增一筆資料 ps = conn.prepareStatement("INSERT INTO MyTable(id, name) VALUES (?, ?)"); ps.setString(1, "2000159"); ps.setString(2, "Eddie"); ps.executeUpdate(); // 修改一筆資料 ps = conn.prepareStatement("UPDATE MyTable SET name = ? WHERE id = ? "); ps.setString(1, "Eddie2"); ps.setString(2, "2000159"); ps.executeUpdate(); // 刪除一筆資料 ps = conn.prepareStatement("DELETE FROM MyTable WHERE id = ?"); ps.setString(1, "2000159"); ps.executeUpdate(); // 查詢一筆資料 ps = conn.prepareStatement("SELECT * FROM MyTable WHERE id = ?"); // 填入參數 ps.setString(1, "2000159"); // 執行 rs = ps.executeQuery(); // 確認結果集是否為空 while (rs.next()) { // 取得一筆資料的欄位數量 int numColumns = rs.getMetaData().getColumnCount(); // 使用迴圈將資料印出 for (int i = 1; i <= numColumns; i++) { System.out.println("COLUMN " + i + " = " + rs.getObject(i)); } } ``` 原始碼 * JDBCDemo.java ```java=1 package jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class JDBCDemo { public static void main(String[] args) throws SQLException { Connection conn = null; // 使用 Class.forName() 載入 Driver 給 DriverManager try { Class.forName("com.mysql.cj.jdbc.Driver"); System.out.println("Driver loaded!"); } catch (ClassNotFoundException e) { System.out.println("找不到驅動程式類別"); e.printStackTrace(); } // 再使用 DriverManager.getConnection() 取得有效的 Connection 物件 // jdbc 格式 : jdbc:DB 廠商名/辨識字詞:URL try { conn = DriverManager.getConnection ( "jdbc:mysql://localhost:3306/mysql?serverTimezone=UTC", // URL "root", // 用户名 "********" // 密碼 ); } catch (SQLException e) { System.out.println("連線失敗"); e.printStackTrace(); } // 建立 Statement 物件 Statement stmt = conn.createStatement(); // 新增一筆資料 stmt.executeUpdate("INSERT INTO MyTable( name ) VALUES ( 'my name' ) "); // 修改一筆資料 stmt.executeUpdate("UPDATE MyTable SET name = 'Another name' WHERE name = 'my name' "); // 刪除一筆資料 stmt.executeUpdate("DELETE FROM MyTable WHERE name = 'Another name' "); // 查詢一筆資料 ResultSet rs = stmt.executeQuery("SELECT * FROM MyTable"); // 確認結果集是否為空 while (rs.next()) { // 取得一筆資料的欄位數量 int numColumns = rs.getMetaData().getColumnCount(); // 使用迴圈將資料印出 for (int i = 1; i <= numColumns; i++) { System.out.println("COLUMN " + i + " = " + rs.getObject(i)); } } // 記得關閉物件 rs.close(); stmt.close(); /* 實務上為了防止 SQL Injection 而偏好使用能夠將 查詢條件參數化的 PreparedStatement 物件 */ // 建立 PreparedStatement 物件 PreparedStatement ps = null; // 新增一筆資料 ps = conn.prepareStatement("INSERT INTO MyTable(id, name) VALUES (?1, ?2)"); ps.setString(1, "2000159"); ps.setString(2, "Eddie"); rs = ps.executeUpdate(); // 修改一筆資料 ps = conn.prepareStatement("UPDATE MyTable SET name = ?1 WHERE id = ?2 "); ps.setString(1, "Eddie2"); ps.setString(2, "2000159"); rs = ps.executeUpdate(); // 刪除一筆資料 ps = conn.prepareStatement("DELETE FROM MyTable WHERE id = ?1"); ps.setString(1, "2000159"); rs = ps.executeUpdate(); // 查詢一筆資料 // 使用問號作為參數 ps = conn.prepareStatement("SELECT * FROM MyTable WHERE id = ?"); // 填入參數 ps.setString(1, "2000159"); // 執行 rs = ps.executeQuery(); // 確認結果集是否為空 while (rs.next()) { // 取得一筆資料的欄位數量 int numColumns = rs.getMetaData().getColumnCount(); // 使用迴圈將資料印出 for (int i = 1; i <= numColumns; i++) { System.out.println("COLUMN " + i + " = " + rs.getObject(i)); } } } } ``` ### JDBC Template - 建立 Spring Boot 專案 我們先建立一個簡易的 Spring Boot Web 專案 並選擇需要採用的 dependencies ![1](https://i.imgur.com/fCXi9y2.png) 如果要手動加入 Spring JDBC 依賴,需要在 pom.xml 加入 ```xml= <!-- JDBC API --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> ``` 專案結構如下 ![2](https://i.imgur.com/RI2o0Gg.png) #### JDBC Template 部分 Application.properties DB 連線設定參數 ```java= spring.datasource.url=jdbc:mysql://localhost:3306/mysql?serverTimezone=UTC spring.datasource.username=root spring.datasource.password=******** ``` ##### Controller * DemoController.java ```java= package com.systex.demo.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import com.systex.demo.db.MyTableDAOImpl; import com.systex.demo.db.People; @RestController public class DemoController { // 注入 DAO 物件 @Autowired MyTableDAOImpl dao; /* 新增接口 * Method : post * parameter : People * return : String */ @PostMapping("/add") public String create(@RequestBody People people) { dao.addPeople(people); return "OK"; } /* 刪除接口 * Method : get * parameter : String * return : String */ @GetMapping("/delete/{name}") public String delete(@PathVariable String name) { dao.deletePeople(name); return "OK"; } /* 查詢接口 * Method : get * parameter : String * return : String */ @GetMapping("/search/{id}") public String search(@PathVariable String id) { People p = dao.searchPeople(id); return p.getName(); } /* 修改接口 * Method : post * parameter : People * return : String */ @PostMapping("/update") public String update(@RequestBody People people) { dao.modifyPeople(people); return "OK"; } } ``` ##### DAO 介面與對應資料表的物件 * IMyTableDAO.java 定義新刪查改方法 ```java= package com.systex.demo.db; public interface IMyTableDAO { // 新增人員 public void addPeople(People p); // 刪除人員 public void deletePeople(String name); // 查詢人員 public People searchPeople(String id); // 修改人員 public void modifyPeople(People p); } ``` * People.java 定義資料庫結構 ```java= package com.systex.demo.db; public class People { // 欄位 : 人員 ID private String id; // 欄位 : 人員姓名 private String name; public void setId(String id) { this.id = id; } public String getId() { return id; } public void setName(String name) { this.name = name; } public String getName() { return name; } } ``` ##### 實作 DAO 介面 * MyTableDAOImpl.java ```java= package com.systex.demo.db; import java.sql.ResultSet; import java.sql.SQLException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowCallbackHandler; import org.springframework.stereotype.Service; @Service public class MyTableDAOImpl implements IMyTableDAO { public MyTableDAOImpl() { } // 使用 Spring bean 自動注入 jdbcTemplate 物件 @Autowired private JdbcTemplate jdbcTemplate; public void addPeople(People p) { String SQL = "INSERT INTO MyTable(id, name) VALUES (?,?) "; jdbcTemplate.update(SQL, p.getId(), p.getName()); } @SuppressWarnings("deprecation") public People searchPeople(String id) { final People people = new People(); jdbcTemplate.query("SELECT * FROM MyTable WHERE id = ?", new Object[] { id }, new RowCallbackHandler() { public void processRow(ResultSet rs) throws SQLException { System.out.println(rs.getString("id")); System.out.println(rs.getString("name")); people.setId(rs.getString("id")); people.setName(rs.getString("name")); } }); return people; }; public void deletePeople(String name) { String SQL = "DELETE FROM MyTable WHERE name = ? "; jdbcTemplate.update(SQL, name); }; public void modifyPeople(People p) { String SQL = "Update MyTable SET name = ? WHERE id = ?"; jdbcTemplate.update(SQL, p.getName(), p.getId()); }; } ``` ##### 細部說明 - 新增 新增 Method 使用 jdbcTemplate.update(SQL 指令 (String),參數 (any)args[]) ```java= public void addPeople(People p) { String SQL = "INSERT INTO MyTable(id, name) VALUES (?,?) "; jdbcTemplate.update(SQL, p.getId(), p.getName()); } ``` 執行結果 ![1](https://i.imgur.com/NcKPfJc.png) ##### 細部說明 - 刪除 新增 Method 使用 jdbcTemplate.update(SQL 指令 (String),參數 (any)args[]) ```java= public void deletePeople(String name) { String SQL = "DELETE FROM MyTable WHERE name = ? "; jdbcTemplate.update(SQL, name); }; ``` 執行結果 ![2](https://i.imgur.com/eUfXmj9.png) ##### 細部說明 - 查詢 新增 Method 使用 jdbcTemplate.update(SQL 指令 (String),參數 (any)args[]) ```java= public People searchPeople(String id) { String SQL = "SELECT * FROM MyTable WHERE id = ?"; // 這裡使用 Mapper 自動映射,不擅使用的話也可以使用 jdbcTemplate.queryForList() 自行包裝 People 物件 @SuppressWarnings("deprecation") public People searchPeople(String id) { People people = new People(); jdbcTemplate.query("SELECT * FROM MyTable WHERE id = ?", new Object[] { id }, new RowCallbackHandler() { public void processRow(ResultSet rs) throws SQLException { people.setId(rs.getString("id")); people.setName(rs.getString("name")); } }); return people; }; ``` 執行結果 ![1](https://i.imgur.com/T9Vbey0.png) ##### 細部說明 - 修改 新增 Method 使用 jdbcTemplate.update(SQL 指令 (String),參數 (any)args[]) ```java= public void modifyPeople(People p) { String SQL = "Update MyTable SET name = ? WHERE id = ?"; jdbcTemplate.update(SQL, p.getName(), p.getId()); }; ``` 執行結果 ![1](https://i.imgur.com/EFtSY9O.png) --- ## 參考資料 * [關於 JdbcTemplate 你只需要這一篇](https://medium.com/@steph.c/jdbctemplate-%E7%AF%84%E4%BE%8B-2c9a1f3718ba) * [使用 JdbcTemplate](https://openhome.cc/Gossip/SpringGossip/UseJdbcTemplate.html) * [Java Tutorial 第三堂(2)使用 spring-jdbc 存取資料庫](https://openhome.cc/Gossip/CodeData/JavaTutorial/SpringJdbc.html) ## 撰寫紀錄 | 人員 | 日期 | 修改紀錄 | | - | - | - | | 懿修 | 2022/7 | 初版 |

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully