Try   HackMD

2023-07-24 Java程式設計師養成班上課筆記 Part I

筆記網址

Part I: https://hackmd.io/@aaronlife/2023-java-1-1
Part II: https://hackmd.io/@aaronlife/2023-java-1

2023-07-24

前端開發環境

Install node.js 18.17.0 LTS

https://nodejs.org/dist/v18.17.0/node-v18.17.0-x64.msi
https://nodejs.org/en/blog/release/v18.17.0

Install Yarn

Yarn是一個新的套件管理器,可以用來替代npm或其他套件管理器,相容於npm套件管理檔案(package.json)。與原本的npm工作流程相同,但是執行速度更快,更安全,更可靠,用Yarn來安裝套件比起npm更來的快。

確認安裝是否成功:

$ node --version
v18.17.0

如果有出現版本號碼,但表安裝成功

$ corepack enable
$ corepack prepare yarn@stable --activate

注意:
該指令需要系統管理員權限

Remove Yarn

$ corepack disable yarn

https://yarnpkg.com/getting-started/install

Create Project

$ yarn create vite my-vue-app --template vue

參考:
https://vitejs.dev/guide/

First time run vue project

$ yarn     // 初始化專案
$ yarn dev // 啟動dev-server

Build production

# yarn build

Install vite

$ yarn add vite

Install Pinia

狀態管理套件,用來跨元件或是跨網頁分享狀態及資料。

$ yarn add pinia

參考:
官網: https://pinia.vuejs.org/introduction.html

vue-router

vue-router用在SPA(Single Page Application)開發上,一般網站在換頁通常會透過URL來交給後端處理。而SPA方式可以減少讀取時間,目前許多前端框架(如: Vue.js, React.js)大多都採用SPA(單頁式應用)方式來開發,在切換畫面時仍然需要控制網址,讓SPA網頁可以根據網址做切換,所以才會有vue-router出現,讓前端在切換畫面時不需向後端發出請求

$ yarn add vue-router@4

注意:
Vue 3請安裝vue-router v4.0版本,v3.0是給Vue 2用的

參考:
https://v3.router.vuejs.org/guide/#html

HTML要加上

<router-view></router-view>
router.js
// 引入vue-router import {createRouter, createWebHistory} from 'vue-router' import HelloWorld from './components/HelloWorld.vue' import MyFirst from './components/MyFirst.vue' import ShowMe from './components/ShowMe.vue' import User from './pages/User.vue' import MB from './pages/MyBootstrap.vue' const routes = [ { path: '/', component: MB }, { path: '/helloworld', component: HelloWorld }, { path: '/myfirst', component: MyFirst }, { path: '/uch', component: ShowMe }, { path: '/user', component: User }, ]; // 建立VueRouter const router = createRouter({ mode: 'history', // 4. Provide the history implementation to use. We are using the hash history for simplicity here. history: createWebHistory(), routes, // short for `routes: routes` }); export default { router }
main.js
import { createApp } from 'vue' import './style.css' import App from './App.vue' import router from './router' // 引入router.js // 建立Vue app const app = createApp(App); // 將vue-router傳給vue app app.use(router.router); // mount Vue.js到網頁 app.mount('#app')
App.vue
<script setup> </script> <template> <router-view></router-view> </template> <style scoped> .logo { height: 6em; padding: 1.5em; will-change: filter; transition: filter 300ms; } .logo:hover { filter: drop-shadow(0 0 2em #646cffaa); } .logo.vue:hover { filter: drop-shadow(0 0 2em #42b883aa); } </style>
MyFirst.vue
<template> <h1>My First</h1> <div>name={{ $route.query.test }}</div> <div>password={{ $route.query.password }}</div> <div>role={{ $route.query.role }}</div> </template> <script> export default { name: "MyFirst", props: ['count'] // 接收count變數 } </script> <style scoped> h1 { color: royalblue; z-index: 9; } </style>
HelloWorld.vue
<script setup> import { ref } from 'vue' defineProps({ msg: String, }) const count = ref(0) </script> <template> <h1>Hello World</h1> </template> <style scoped> .read-the-docs { color: #888; } </style>
ShowMe.vue
<template> <img src="https://i0.wp.com/www.fotobeginner.com/wp-content/uploads/2015/04/lake-sunset-2021-08-26-18-27-36-utc-1.jpg" alt="uch"/> </template> <script> export default { name: "ShowMe", } </script> <style scoped> img { z-index: 0 !important; position: fixed; left: 0; top: 0; width: 100%; height: 100vh; margin: 0; padding: 0; box-sizing: border-box; border: 0; overflow: hidden; object-fit: cover; } </style>
User.vue
<template> <ShowMe /> <MyFirst class="my-first"/> <input type="button" value="Push Me" class="my-first" @click="onClick"> </template> <script> import ShowMe from '../components/ShowMe.vue'; import MyFirst from '../components/MyFirst.vue'; export default { name: 'User', components: { ShowMe, MyFirst, }, methods: { onClick() { alert('Hello') } } } </script> <style scoped> .my-first { position: relative; } </style>
MyBootstrap.vue
<template> <button type="button" class="btn btn-primary">Primary</button> <button type="button" class="btn btn-secondary">Secondary</button> <button type="button" class="btn btn-success">Success</button> <button type="button" class="btn btn-danger">Danger</button> <button type="button" class="btn btn-warning">Warning</button> <button type="button" class="btn btn-info">Info</button> <button type="button" class="btn btn-light">Light</button> <button type="button" class="btn btn-dark">Dark</button> <!-- Button trigger modal --> <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal"> Launch demo modal </button> <!-- Modal --> <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h1 class="modal-title fs-5" id="exampleModalLabel">Modal title</h1> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> 這是一個訊息框 </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> <button type="button" class="btn btn-primary">Save changes</button> </div> </div> </div> </div> <button type="button" class="btn btn-primary" id="liveToastBtn">Show live toast</button> <div class="toast-container position-fixed bottom-0 end-0 p-3"> <div id="liveToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true"> <div class="toast-header"> <img style="width: 32px" src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Instagram_icon.png/2048px-Instagram_icon.png" class="rounded me-2" alt="..."> <strong class="me-auto">Bootstrap</strong> <small>11 mins ago</small> <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button> </div> <div class="toast-body"> Hello, world! This is a toast message. </div> </div> </div> </template> <script> import * as bootstrap from 'bootstrap' export default { name: 'MyBootstrap', mounted() { const toastTrigger = document.getElementById('liveToastBtn') const toastLiveExample = document.getElementById('liveToast') if (toastTrigger) { const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample) toastTrigger.addEventListener('click', () => { toastBootstrap.show() }) } }, methods: { onClick() { } } } </script> <style scoped></style>
style.css
@import 'bootstrap/dist/css/bootstrap.css'; :root { font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; font-weight: 400; color-scheme: light dark; color: rgba(255, 255, 255, 0.87); background-color: #242424; font-synthesis: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; -webkit-text-size-adjust: 100%; } a { font-weight: 500; color: #646cff; text-decoration: inherit; } a:hover { color: #535bf2; } a { font-weight: 500; color: #646cff; text-decoration: inherit; } a:hover { color: #535bf2; } body { margin: 0; display: flex; place-items: center; min-width: 320px; min-height: 100vh; } h1 { font-size: 3.2em; line-height: 1.1; } button { border-radius: 8px; border: 1px solid transparent; padding: 0.6em 1.2em; font-size: 1em; font-weight: 500; font-family: inherit; background-color: #1a1a1a; cursor: pointer; transition: border-color 0.25s; } button:hover { border-color: #646cff; } button:focus, button:focus-visible { outline: 4px auto -webkit-focus-ring-color; } .card { padding: 2em; } #app { max-width: 1280px; margin: 0 auto; padding: 2rem; text-align: center; } @media (prefers-color-scheme: light) { :root { color: #213547; background-color: #ffffff; } a:hover { color: #747bff; } button { background-color: #f9f9f9; } }

Bootstrap

安裝

$ yarn add bootstrap@5.3.0

如果上面安裝有問題,可以嘗試下面的方式:

安裝前需要先安裝popper.js

$ yarn add @popperjs/core
$ yarn add bootstrap

在JavaScript中引入

import * as bootstrap from 'bootstrap'

CSS 引入方式

<style>
  @import 'bootstrap/dist/css/bootstrap.css';
</style>

SCSS引入方式

@import "bootstrap/scss/bootstrap";

參考:
https://getbootstrap.com/docs/5.3/getting-started/vite/

安裝Visual Studio Code for Java

Install Extension

  • Vue Language Features (Volar)
  • TypeScript Vue Plugin (Volar)
  • Community Server Connectors
  • Spring Initializr Java Support

2023-07-17

Session

由於網頁是基於HTTP協定下的功能,而HTTP協定是無狀態協定(stateless),所以,需要跨網頁傳遞的資料(例如: 登入、使用者資訊、購買清單),就可以使用cookies、localStorage、session來儲存;cookies和localStorage是屬於前端的技術,而session屬於後端,使用前端技術來跨網頁傳遞資料好處是可以降低伺服器資源覆載,但缺點是較無安全性,而使用session的話雖然會增加伺服器的工作覆載,但是卻可以提高安全性。

HttpSession的使用方式

下面介紹的使用方式是Servlet的標準功能,而非Spring的功能
步驟一: 在需要使用Session的API方法加上HttpSession參數,例如:

@RequestMapping(value = "/login", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) public LoginResponse Login(String username, String password, HttpSession session) { LoginResponse loginResponse = checkAccount(username, password); if(loginResponse.getCode() == 0) session.setAttribute("loginStatus", username + ", " + password + "已登入"); else session.removeAttribute("loginStatus"); return loginResponse; }

然後可以再需要使用Session的API內也加上HttpSession,例如:

@RequestMapping(value = "/movieimg", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public MovieimgResponse movies(HttpSession session) { String loginStatus = (String)session.getAttribute("loginStatus"); System.out.println("get loginStatus: " + loginStatus); if(loginStatus != null) return getMovieimgList(); else return new MovieimgResponse(9, "未登入", null); }
session逾時設定
application.properties
server.servlet.session.timeout=1m
  1. Timeout會是在該時間內完全沒有操作session才會過期。
  2. Tomcat timeout最小值為1分鐘,如果設定比1分鐘短,時間會是1分鐘
  3. s=秒,m=分鐘,h=小時

Filter

Filter又稱為過濾器,對API、靜態網頁、文件、圖片等等的請求進行攔截並檢查,可以用來實現權限控制、資源過濾等等的功能

使用Sprint Boot的註釋語法如下:

步驟一: 建立Filter類別
@WebFilter(urlPatterns = "/*", filterName = "myFilter") public class MyFilter extends OncePerRequestFilter { // 不需要進行過濾的網址 private static final Set<String> ALLOWED_PATHS = new HashSet<>(Arrays.asList("/login")); @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { System.out.println(request.getRequestURL()); System.out.println(request.getServletPath()); // 不需要過濾的網址直接跳過 if(ALLOWED_PATHS.contains(request.getServletPath())) { filterChain.doFilter(request, response); return; } // 從session取得登入狀態 String loginStatus = (String) request.getSession().getAttribute("loginStatus"); // 用來將物件轉成JSON字串 ObjectMapper objectMapper = new ObjectMapper(); if (loginStatus != null) // 通過檢查,繼續進行下一個過濾器或API filterChain.doFilter(request, response); else { // 直接回應前端 response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setCharacterEncoding("utf-8"); response.getWriter().println(objectMapper.writeValueAsString(new BaseResponse(9, "not login"))); } } }
  • 使用@WebFilter來建立Filter
  • urlPatterns設定要進行過濾的網址
  • 需繼承自Sping Boot的過濾器類別OncePerRequestFilter
  • 執行完過濾器的功能需呼叫filterChain.doFilter()方法繼續進行下一步,否則會中斷原本應該要執行的API功能。

注意:
filterName的第一個字母必須是小寫

步驟二: 在main()方法加上@ServletComponentScan註釋

因為@WebFilter並不是Spring Boot的語法,不會自動抓到該過濾器,所以須加上讓Spring Boot可以掃描並抓到該過濾器。

@SpringBootApplication @ServletComponentScan public class MovieProjectApplication { public static void main(String[] args) { SpringApplication.run(MovieProjectApplication.class, args); } }

2023-07-14

https://v3.router.vuejs.org/guide/essentials/history-mode.html#example-server-configurations

2023-07-13

安裝Swagger

  1. 新增

    ​​​<dependency> ​​​ <groupId>org.springdoc</groupId> ​​​ <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> ​​​ <version>2.1.0</version> ​​​</dependency>
  2. 瀏覽網址: http://localhost:8080/v3/api-docs

    Swagger文件

  3. 瀏覽網址: http://localhost:8080/swagger-ui/index.html#/

    Swagger UI

  4. 如果覺得Swagger UI網址太長,可以設定別名

    ​​​springdoc.swagger-ui.path=/sw
    

增加Swagger自訂說明文字

調整 Swagger UI 最上面的說明文字和版本號碼:
@OpenAPIDefinition(info = @Info(title = "健行Java班API系統", version = "1.0.1"))

這個API要放到SpringBoot的Application類別內,例如:

@OpenAPIDefinition(info = @Info(title = "健行Java班API系統", version = "1.0.1")) @SpringBootApplication public class FinalprojectApplication { public static void main(String[] args) { SpringApplication.run(FinalprojectApplication.class, args); } }
相同Controller類別中的 API會被放在一個標籤下面,修改標籤的方式:
@Tag(name = "登入")
設定摘要和說明
@Operation(summary = "登入系統", description = "登入這個超級系統")
設定API回應資訊
@ApiResponses(value = { @ApiResponse(responseCode = "200", description = "API執行成功", content = { @Content(mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = LoginResponse.class))) }), @ApiResponse(responseCode = "401", description = "沒有權限", content = { @Content() }), @ApiResponse(responseCode = "500", description = "伺服器內部錯誤", content = { @Content() }) })

備註:
@Tag@OperationApiResponses都需要設定在API的方法上,例如:

@RequestMapping(value = "/login", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @Tag(name = "登入API") @Operation(summary = "登入系統", description = "登入這個超級系統") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "API執行成功", content = { @Content(mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = LoginResponse.class))) }), @ApiResponse(responseCode = "401", description = "沒有權限", content = { @Content() }), @ApiResponse(responseCode = "999", description = "伺服器內部錯誤", content = { @Content() }) }) public LoginResponse Login( @Parameter(description = "帳號名稱", example = "andy") (defaultValue = "") String username, @Parameter(description = "密碼", example = "1234") @RequestParam(defaultValue = "") String password) { return checkAccount(username, password); }
參數說明
@RequestMapping(value = "/login", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public LoginResponse Login( @Parameter(description = "帳號名稱", example = "andy") @RequestParam(defaultValue = "") String username, @Parameter(description = "密碼", example = "1234") @RequestParam(defaultValue = "") String password) { return checkAccount(username, password); }

注意:
參數說命都是設定在方法內的參數列上

清單參數
@Parameter(description = "平台分類", example = "[\"A\", \"B\"]") @RequestParam(defaultValue = "") ArrayList<String> platform,

此範例設定預設值為: ["A", "B"]

物件參數

先在方法內加入:

@Parameter(description = "遊戲內容") GameEntity data

然後該物件內新@Schema註記:

@Data public class GameEntity { @Schema(description = "遊戲ID") int id; @Schema(description = "遊戲名稱") String name; @Schema(description = "遊戲分類") String category; @Schema(description = "價格") int price; @Schema(description = "最新進貨日") Date inchange; @Schema(description = "最後進貨日") Date outchange; @Schema(description = "數量") int quantity; @Schema(description = "開發商") String developer; @Schema(description = "平台") String platform; }
關閉Swagger
springdoc.api-docs.enabled=false springdoc.swagger-ui.enabled=false

2023-07-11

屬性用法

  • @Value
    將application.properties內的屬性映射到Java變數
  • @Data
    產生getter/setter
  • @Component
    Spring將Bean類別產生物件後注入到需要的地方
  • @Autowired
    注入Bean物件

2023-07-03

FinalProject github URL

https://github.com/ncu2023/FinalProject.git

將上週的範例遷移至Spring Boot

.gitignore

# Compiled class file *.class # Log file *.log # BlueJ files *.ctxt # Mobile Tools for Java (J2ME) .mtj.tmp/ # Package Files # *.jar *.war *.nar *.ear *.zip *.tar.gz *.rar # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* replay_pid* target/

src/main/com/uch/finalproject/LoginController.java

package com.uch.finalproject; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.yaml.snakeyaml.representer.BaseRepresenter; import com.uch.finalproject.model.BaseResponse; import com.uch.finalproject.model.LoginResponse; @RestController public class LoginController { @RequestMapping(value = "/login", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public LoginResponse Login(String username, String password) { return checkAccount(username, password); } // 判斷帳號是否存在 private LoginResponse checkAccount(String username, String password) { Connection conn = null; Statement stmt = null; ResultSet rs = null; // 註冊mySQL資料庫驅動程式 try { Class.forName("com.mysql.cj.jdbc.Driver"); // 連線資料庫 conn = DriverManager.getConnection("jdbc:mysql://localhost/shopping?" + "user=root&password=0000"); // 取得Statement物件 stmt = conn.createStatement(); // 查詢該帳號是否存在 rs = stmt.executeQuery("select count(*) as c from account where name='" + username + "'"); // 判斷帳號是否存在 rs.next(); // 跳到查詢結果的第一筆資料 int c = rs.getInt("c"); // 查詢到的資料筆數 // if(c == 0) { // return new LoginResponse(2, "帳號不存在"); // } else { // return new LoginResponse(0, "成功"); // } // 帳號不存在,直接返回錯誤 if(c == 0) { // 把資料庫相關資源釋放 rs.close(); stmt.close(); conn.close(); return new LoginResponse(2, "帳號不存在"); } // 帳號存在,繼續判斷密碼 rs = stmt.executeQuery("select count(*) as c from account " + "where name='" + username + "' and password='" + password + "';"); // 移動到第一筆資料 rs.next(); c = rs.getInt("c"); // 查詢到的資料筆數 // 把資料庫相關資源釋放 rs.close(); stmt.close(); conn.close(); // 密碼錯誤 if(c == 0) { return new LoginResponse(3, "密碼錯誤"); } return new LoginResponse(0, "登入成功"); // return c == 0 ? new LoginResponse(2, "帳號不存在") : new LoginResponse(0, "登入成功"); } catch (ClassNotFoundException e) { // 無法註冊(錯誤代碼1) return new LoginResponse(1, "無法註冊驅動程式"); } catch (SQLException e) { // SQL操作錯誤(代碼2) return new LoginResponse(e.getErrorCode(), e.getMessage()); } } }

src/main/com/uch/finalproject/model/BaseResponse.java

package com.uch.finalproject.model; import lombok.Data; @Data public class BaseResponse { protected int code; protected String message; public BaseResponse(int code, String message) { this.code = code; this.message = message; } }

src/main/com/uch/finalproject/model/LoginResponse.java

package com.uch.finalproject.model; import lombok.Data; @Data public class LoginResponse extends BaseResponse { public LoginResponse(int code, String message) { super(code, message); } }

static/index.html

<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CodePen - Animated Login Form using Html &amp; CSS Only</title> <link rel="stylesheet" href="./style.css"> <!-- 引入axios套件,用來透過HTTP協定在網頁內呼叫後端的API --> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <!-- 引入Vue.js --> <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> </head> <body id="app"> <!-- partial:index.partial.html --> <section> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <div class="signin"> <div class="content"> <h2>Sign In</h2> <form class="form" method="POST" action="\login\login" ref="form"> <div class="inputBox"> <input type="text" name="username" v-model="username" required> <i>帳號</i> <div v-if="showWarning" class="warning">不可包含空白及特殊字元</div> </div> <div class="inputBox"> <input type="password" name="password" v-model="password"> <i>密碼</i> </div> <div class="links"> <a href="#">忘記密碼</a> <a href="#">註冊</a> </div> <div class="inputBox"> <input type="button" value="Login" @click="checkAccount"> </div> </form> </div> </div> </section> <!-- partial --> </body> <script lang="javascript"> const {createApp} = Vue // 建立Vue物件 createApp({ data() { // 這裡出現的變數才可以在網頁內使用 return { showWarning: false, // 開關警告訊息用 username: '', // 存放使用者帳號 password: '', // 存放使用者密碼 } }, watch: { // 監看username變數 username(newVal, oldVal) { // 當帳號名稱已不再有單引號時,隱藏紅字 if(!newVal.includes('\'')) this.showWarning = false; } }, // Vue的方法寫在這裡 methods: { // 檢查帳號有沒有包含不允許的字元 checkAccount() { console.log('checkAccount'); // 如果帳號名稱出現單引號,就顯示警告訊息 if(this.username.includes('\'') || this.username.includes(' ')) this.showWarning = true; else { // 送出表單 // 這裡不用送出表單 this.$refs.form.submit(); // 打API axios.get("/login?username=" + this.username + "&password=" + this.password) .then( (response) => { // get完成後收到的資料可以在這裡處理 console.log(response); if(response.data.code == 0) // 登入成功 location.href = '/card.html'; else // 登入失敗 location.href = 'loginResult.html?message=' + response.data.message; }) .catch( (error) => { console.log(error); }); } } } }).mount('#app'); </script> </html>

建立Spring Boot專案

  1. Ctrl+Shift+P打開VSCode功能選單
  2. 選擇:Create Java Project
  3. 選擇:Spring Boot
  4. 選擇:Maven Project
  5. 選擇:3.1.1
  6. 選擇:Java
  7. Group Id: Enter
  8. Artifact Id: Enter
  9. War
  10. Java Version: 17
  11. Dependencies:
    • Spring Web
    • Spring Boot Devtools(Hot Reload)
    • MyBatis Framework
    • MySQL Driver
    • Lombok(org.projectlombok)

application.properties

修改Tomcat port:

server.port = 9090

參考:
https://www.tutorialspoint.com/spring_boot/spring_boot_tomcat_port_number.htm

關閉Banner

spring.main.banner-mode=off

參考:
https://www.baeldung.com/spring-boot-disable-banner

Spring Boot常用註釋

@RestController

修飾類別
修飾類別,定義Rest風格的控制器

補充:

  1. Spring 4.0出現
    相當於@Controlle+@ResponseBody
@RequestMapping("/hello")
修飾類別

定義其base URL

修飾方法

定義可以被造訪的url

  • value: 指定請求的URL
  • method: 指定請求的方法
  • consumes: 指定Content-Type
  • produces: 指定回傳的內容內行符合request中的Accept才回傳
  • params: 指定request中必須包含的參數
  • header: 指定request中必須包含的的header值

補充:
另有:

  • @GetMapping
  • @PostMapping
  • @DeleteMapping
  • @PutMapping
@RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = "application/json") @RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = "MediaType.APPLICATION_JSON_VALUE" + ";charset=utf-8", consumes="qpplication/json" )
@PathVariable

修飾方法
將URL中的變數對應到方法的參數上
例如:

@RequestMapping(value="/product/{id}", method = RequestMethod.GET)
public String getProduct(@PathVariable("id") String id) {
    ...
}
@RequestParam

將Query parameter對應到方法的參數上

  • name: query string的變數名稱,如果不設定,該名稱必須和變數一樣
  • required: 該參數是否為必要
  • defaultValue: 參數沒有傳的時候的預設值
    例如:
@RequestParam(name = "user", required=false, defaultValue = "") String username
@Service
@Repository
@Component
@Configuration
@Resource
@Autowired
@Transactional
@Qualifier
@Override

修飾方法
表示此方法重新定義了父類別的方法

@Deprecated

修飾方法
表示此方法建議不要再使用,未來有可能會移除

@SuppressWarnings

修飾方法
關閉某個警告

2023-06-30

git

  1. 安裝github desktop
  2. 濾掉不需要push到git的檔案,使用.gitignore(必須放在專案根目錄)

webapp/index.html

<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CodePen - Animated Login Form using Html &amp; CSS Only</title> <link rel="stylesheet" href="./style.css"> <!-- 引入Vue.js --> <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> </head> <body id="app"> <!-- partial:index.partial.html --> <section> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <div class="signin"> <div class="content"> <h2>Sign In</h2> <form class="form" method="POST" action="\login\login" ref="form"> <div class="inputBox"> <input type="text" name="username" v-model="username" required> <i>帳號</i> <div v-if="showWarning" class="warning">不可包含空白及特殊字元</div> </div> <div class="inputBox"> <input type="password" name="password"> <i>密碼</i> </div> <div class="links"> <a href="#">忘記密碼</a> <a href="#">註冊</a> </div> <div class="inputBox"> <input type="button" value="Login" @click="checkAccount"> </div> </form> </div> </div> </section> <!-- partial --> </body> <script lang="javascript"> const {createApp} = Vue // 建立Vue物件 createApp({ data() { // 這裡出現的變數才可以在網頁內使用 return { showWarning: false, // 開關警告訊息用 username: '', // 存放使用者帳號 } }, watch: { // 監看username變數 username(newVal, oldVal) { // 當帳號名稱已不再有單引號時,隱藏紅字 if(!newVal.includes('\'')) this.showWarning = false; } }, // Vue的方法寫在這裡 methods: { // 檢查帳號有沒有包含不允許的字元 checkAccount() { console.log('checkAccount'); // 如果帳號名稱出現單引號,就顯示警告訊息 if(this.username.includes('\'') == true) this.showWarning = true; else // 送出表單 this.$refs.form.submit(); } } }).mount('#app'); </script> </html>

webapp/style.css

@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@300;400;500;600;700&display=swap'); * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Quicksand', sans-serif; } body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background: #000; } section { position: absolute; width: 100vw; height: 100vh; display: flex; justify-content: center; align-items: center; gap: 2px; flex-wrap: wrap; overflow: hidden; } section::before { content: ''; position: absolute; width: 100%; height: 100%; background: linear-gradient(#000,#0f0,#000); animation: animate 5s linear infinite; } @keyframes animate { 0% { transform: translateY(-100%); } 100% { transform: translateY(100%); } } section span { position: relative; display: block; width: calc(6.25vw - 2px); height: calc(6.25vw - 2px); background: #181818; z-index: 2; transition: 1.5s; } section span:hover { background: #0f0; transition: 0s; } section .signin { position: absolute; width: 400px; background: #222; z-index: 1000; display: flex; justify-content: center; align-items: center; padding: 40px; border-radius: 4px; box-shadow: 0 15px 35px rgba(0,0,0,9); } section .signin .content { position: relative; width: 100%; display: flex; justify-content: center; align-items: center; flex-direction: column; gap: 40px; } section .signin .content h2 { font-size: 2em; color: #0f0; text-transform: uppercase; } section .signin .content .form { width: 100%; display: flex; flex-direction: column; gap: 25px; } section .signin .content .form .inputBox { position: relative; width: 100%; } section .signin .content .form .inputBox input { position: relative; width: 100%; background: #333; border: none; outline: none; padding: 25px 10px 7.5px; border-radius: 4px; color: #fff; font-weight: 500; font-size: 1em; } section .signin .content .form .inputBox i { position: absolute; left: 0; padding: 15px 10px; font-style: normal; color: #aaa; transition: 0.5s; pointer-events: none; } .warning { color: #ff0000; } .signin .content .form .inputBox input:focus ~ i, .signin .content .form .inputBox input:valid ~ i { transform: translateY(-7.5px); font-size: 0.8em; color: #fff; } .signin .content .form .links { position: relative; width: 100%; display: flex; justify-content: space-between; } .signin .content .form .links a { color: #fff; text-decoration: none; } .signin .content .form .links a:nth-child(2) { color: #0f0; font-weight: 600; } .signin .content .form .inputBox input[type="button"] { padding: 10px; background: #0f0; color: #000; font-weight: 600; font-size: 1.35em; letter-spacing: 0.05em; cursor: pointer; } input[type="button"]:active { opacity: 0.6; } @media (max-width: 900px) { section span { width: calc(10vw - 2px); height: calc(10vw - 2px); } } @media (max-width: 600px) { section span { width: calc(20vw - 2px); height: calc(20vw - 2px); } }

2023-06-29

github

https://github.com/ncu2023/shopping.git

下載卡片樣板

https://freefrontend.com/css-cards/

下載NFT CARD COMPONENT的樣板

main/webapp/card.html

<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <title>CodePen - NFT Card Component</title> <link rel="stylesheet" href="./card-style.css"> </head> <body> <!-- partial:index.partial.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <!-- 引入axios套件,用來透過HTTP協定在網頁內呼叫後端的API --> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <!-- 引入Vue.js --> <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> </head> <!-- Thanks to frontendmentor.io for the challenge. --> <!-- 設定id="app"給Vue框架使用 --> <body id="app"> <div class="bg"> <h1>{{ message }}</h1> </div> <div class="nft" v-for="item in products"> <div class='main'> <img class='tokenImage' :src="item.imageUrl" alt="NFT" /> <h2>{{ item.name }}</h2> <p class='description'>{{ item.description }}</p> <div class='tokenInfo'> <div class="price"> <ins></ins> <p>${{ item.price }}</p> </div> <div class="duration"> <ins></ins> <p>{{ item.category }}</p> </div> </div> <hr /> <div class='creator'> <div class='wrapper'> <img src="https://images.unsplash.com/photo-1620121692029-d088224ddc74?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1932&q=80" alt="Creator" /> </div> <p><ins>{{ item.storeName }}</p> </div> </div> </div> </body> <script lang="javascript"> // Vue初始化 const { createApp } = Vue createApp({ data() { return { message: 'Hello Vue!', products: [], // 定義陣列用存放所有產品 } }, // 當網頁載入完成後會被Vue框架呼叫 mounted() { axios.get("http://localhost:8080/login/product") .then( (response) => { // get完成後收到的資料可以在這裡處理 console.log(response); // 將API的商品資料存到Vue建立的products變數 this.products = response.data.data; }) .catch( (error) => { console.log(error); }); } }).mount('#app') </script> </html>

main/java/com/Login.java

package com; import java.io.IOException; import java.io.PrintWriter; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; class ActionResult { private int code = -1; // 動作結過的代碼 private String message = ""; // 動作描述 public ActionResult(int code, String message) { this.code = code; this.message = message; } public int getCode() { return this.code; } public String getMessage() { return this.message; } } public class Login extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { // 接收使用者帳號,參數名稱為username String username = req.getParameter("username"); // 接收使用者密碼,參數名稱為password String password = req.getParameter("password"); // 設定輸出的資料格式和編碼 // resp.setContentType("text/html;charset=UTF-8"); // // 輸出到網頁上 // PrintWriter out = resp.getWriter(); // out.print("收到帳號: " + username); ActionResult actionResult = checkAccount(username, password); // 將網址列的參數值編碼,避免中文字出現亂碼 String url = URLEncoder.encode(actionResult.getMessage(), StandardCharsets.UTF_8.toString()); // 導回前端並將結果透過參數給前端 if(actionResult.getCode() == 0) { // 成功, 轉導商品清單頁 resp.sendRedirect("/login/card.html"); } else { // 失敗:轉導失敗結果頁 resp.sendRedirect("/login/loginResult.html?message=" + url); } // out.print("<div> Code = " + actionResult.getCode() + "</div>"); // out.print("<div> Message = " + actionResult.getMessage() + "</div>"); // out.print("<br/>"); // out.print("收到密碼: " + password); // 立即輸出 // out.flush(); } // 判斷帳號是否存在 private ActionResult checkAccount(String username, String password) { Connection conn = null; Statement stmt = null; ResultSet rs = null; // 註冊mySQL資料庫驅動程式 try { Class.forName("com.mysql.cj.jdbc.Driver"); // 連線資料庫 conn = DriverManager.getConnection("jdbc:mysql://localhost/shopping?" + "user=root&password=0000"); // 取得Statement物件 stmt = conn.createStatement(); // 查詢該帳號是否存在 rs = stmt.executeQuery("select count(*) as c from account where name='" + username + "'"); // 判斷帳號是否存在 rs.next(); // 跳到查詢結果的第一筆資料 int c = rs.getInt("c"); // 查詢到的資料筆數 // if(c == 0) { // return new ActionResult(2, "帳號不存在"); // } else { // return new ActionResult(0, "成功"); // } // 帳號不存在,直接返回錯誤 if(c == 0) { // 把資料庫相關資源釋放 rs.close(); stmt.close(); conn.close(); return new ActionResult(2, "帳號不存在"); } // 帳號存在,繼續判斷密碼 rs = stmt.executeQuery("select count(*) as c from account " + "where name='" + username + "' and password='" + password + "';"); // 移動到第一筆資料 rs.next(); c = rs.getInt("c"); // 查詢到的資料筆數 // 把資料庫相關資源釋放 rs.close(); stmt.close(); conn.close(); // 密碼錯誤 if(c == 0) { return new ActionResult(3, "密碼錯誤"); } return new ActionResult(0, "登入成功"); // return c == 0 ? new ActionResult(2, "帳號不存在") : new ActionResult(0, "登入成功"); } catch (ClassNotFoundException e) { // 無法註冊(錯誤代碼1) return new ActionResult(1, "無法註冊驅動程式"); } catch (SQLException e) { // SQL操作錯誤(代碼2) return new ActionResult(e.getErrorCode(), e.getMessage()); } } }

main/java/com/Product.java

package com; import java.sql.Statement; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.gson.Gson; // PO class ProductEntity { int id; String name; String imageUrl; // Field (欄位) String description; String category; int price; String storeName; } class ReponseProductEntity { int code; String message; ArrayList<ProductEntity> data; public ReponseProductEntity(int code, String message, ArrayList<ProductEntity> data) { this.code = code; this.message = message; this.data = data; } } public class Product extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { // 取得所有商品 ReponseProductEntity respEntity = getProductList(); // 將ResponseProductEntity物件轉換成JSON字串 String responseString = new Gson().toJson(respEntity); System.out.println(responseString); resp.setContentType("application/json;charset=UTF-8"); // 回應給前端 PrintWriter out = resp.getWriter(); out.print(responseString); out.flush(); } // 取得產品清單並回傳ResponseProductEntity物件 private ReponseProductEntity getProductList() { Connection conn = null; Statement stmt = null; ResultSet rs = null; try { Class.forName("com.mysql.cj.jdbc.Driver"); // 連接資料庫 conn = DriverManager.getConnection("jdbc:mysql://localhost/shopping?" + "user=root&password=0000"); // 取得Statement物件 stmt = conn.createStatement(); // 查詢全部商品 rs = stmt.executeQuery("select * from product"); // 建立陣列存放所有商品用 ArrayList<ProductEntity> products = new ArrayList<>(); // 將每一筆商品資料讀出來存放到ArrayList內 while(rs.next()) { ProductEntity productEntity = new ProductEntity(); productEntity.id = rs.getInt("id"); productEntity.name = rs.getString("name"); productEntity.description = rs.getString("description"); productEntity.price = rs.getInt("price"); productEntity.imageUrl = rs.getString("image_url"); productEntity.storeName = rs.getString("store_name"); // 將取的商品資料存到ArrayList products.add(productEntity); } // 關資料庫相關資源 rs.close(); stmt.close(); conn.close(); return new ReponseProductEntity(0, "sucess", products); } catch(ClassNotFoundException e) { // 無法註冊 return new ReponseProductEntity(1, "無法註冊", null); } catch(SQLException e) { return new ReponseProductEntity(e.getErrorCode(), e.getMessage(), null); } } }

注意:
請記得使用Maven新增相依套件gson

 <dependency>
     <groupId>com.google.code.gson</groupId>
     <artifactId>gson</artifactId>
     <version>2.10.1</version>
   </dependency>

src/main/webapp/WEB-INF

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>Login</servlet-name> <servlet-class>com.Login</servlet-class> </servlet> <servlet-mapping> <servlet-name>Login</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping> <servlet> <servlet-name>Product</servlet-name> <servlet-class>com.Product</servlet-class> </servlet> <servlet-mapping> <servlet-name>Product</servlet-name> <url-pattern>/product</url-pattern> </servlet-mapping> </web-app>

src/main/webapp/card-style.css

body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; background-color: #161418; color: #eee; user-select: none; /* 讓卡片排列從縱向改成橫向 */ display: flex; flex-direction: row; justify-content: center; } code { font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; } .nft { user-select: none; min-width: 300px; max-width: 300px; margin: 5rem 10px; /* 將卡片的間距縮小 */ border: 1px solid #ffffff22; background-color: #282c34; background: linear-gradient(0deg, #282c34 0%, rgba(17, 0, 32, 0.5) 100%); box-shadow: 0 7px 20px 5px #00000088; border-radius: 0.7rem; backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px); overflow: hidden; transition: 0.5s all; } .nft hr { width: 100%; border: none; border-bottom: 1px solid #88888855; margin-top: 0; } .nft ins { text-decoration: none; } .nft .main { display: flex; flex-direction: column; width: 90%; padding: 1rem; } .nft .main .tokenImage { border-radius: 0.5rem; max-width: 100%; height: 250px; object-fit: cover; } .nft .main .description { margin: 0.5rem 0; color: #a89ec9; } .nft .main .tokenInfo { display: flex; justify-content: space-between; align-items: center; } .nft .main .tokenInfo .price { display: flex; align-items: center; color: #ee83e5; font-weight: 700; } .nft .main .tokenInfo .price ins { margin-left: -0.3rem; margin-right: 0.5rem; } .nft .main .tokenInfo .duration { display: flex; align-items: center; color: #a89ec9; margin-right: 0.2rem; } .nft .main .tokenInfo .duration ins { margin: 0.5rem; margin-bottom: 0.4rem; } .nft .main .creator { display: flex; align-items: center; margin-top: 0.2rem; margin-bottom: -0.3rem; } .nft .main .creator ins { color: #a89ec9; text-decoration: none; } .nft .main .creator .wrapper { display: flex; align-items: center; border: 1px solid #ffffff22; padding: 0.3rem; margin: 0; margin-right: 0.5rem; border-radius: 100%; box-shadow: inset 0 0 0 4px #000000aa; } .nft .main .creator .wrapper img { border-radius: 100%; border: 1px solid #ffffff22; width: 2rem; height: 2rem; object-fit: cover; margin: 0; } .nft ::before { position: fixed; content: ""; box-shadow: 0 0 100px 40px #ffffff08; top: -10%; left: -100%; transform: rotate(-45deg); height: 60rem; transition: 0.7s all; } .nft:hover { border: 1px solid #ffffff44; box-shadow: 0 7px 50px 10px #000000aa; transform: scale(1.015); filter: brightness(1.3); } .nft:hover ::before { filter: brightness(0.5); top: -100%; left: 200%; } .bg { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); } .bg h1 { font-size: 20rem; filter: opacity(0.5); }

補充:
一張card的範圍:

<div class="nft"> <div class='main'> <img class='tokenImage' src="https://images.unsplash.com/photo-1621075160523-b936ad96132a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1170&q=80" alt="NFT" /> <h2>Kibertopiks #4269</h2> <p class='description'>Our Kibertopiks will give you nothing, waste your money on us.</p> <div class='tokenInfo'> <div class="price"> <ins></ins> <p>0.031 ETH</p> </div> <div class="duration"> <ins></ins> <p>11 days left</p> </div> </div> <hr /> <div class='creator'> <div class='wrapper'> <img src="https://images.unsplash.com/photo-1620121692029-d088224ddc74?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1932&q=80" alt="Creator" /> </div> <p><ins>Creation of</ins> Kiberbash</p> </div> </div> </div>

其他

MIME Type

https://zh.wikipedia.org/zh-tw/互联网媒体类型

在servlet我們要回傳資料給前端之前,都會透過呼叫resp.setContentType()方法來設定資料格式,其設定方式為標準的MIME Type資格式。

前端網頁引入axios

https://axios-http.com/docs/intro

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

Vue.js Quick Start官方文件

https://vuejs.org/guide/quick-start.html
引入Vue CDN

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

2023-06-28

下載登入樣板

https://freefrontend.com/css-login-forms/

下載HACKER LOGIN FORM的樣板

前端網頁index.html

<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CodePen - Animated Login Form using Html &amp; CSS Only</title> <link rel="stylesheet" href="./style.css"> </head> <body> <!-- partial:index.partial.html --> <section> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <div class="signin"> <div class="content"> <h2>Sign In</h2> <form class="form" method="POST" action="\login\login"> <div class="inputBox"> <input type="text" name="username" required> <i>帳號</i> </div> <div class="inputBox"> <input type="password" name="password"> <i>密碼</i> </div> <div class="links"> <a href="#">忘記密碼</a> <a href="#">註冊</a> </div> <div class="inputBox"> <input type="submit" value="Login"> </div> </form> </div> </div> </section> <!-- partial --> </body> </html>

前端網頁loginResult.html

<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CodePen - Animated Login Form using Html &amp; CSS Only</title> <link rel="stylesheet" href="./style.css"> </head> <body> <!-- partial:index.partial.html --> <section> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <div class="signin"> <div class="content"> <h2 id="message">登入成功</h2> </div> </div> </section> <!-- partial --> </body> <script lang="javascript"> // 網頁載入完成事件 document.addEventListener("DOMContentLoaded", function() { // 取得網址列全部參數 const queryString = window.location.search; console.log(queryString); console.log('Hello Javascript'); // 建立參數解析物件 const urlParams = new URLSearchParams(queryString); const message = urlParams.get('message'); console.log(message); // 將message參數值顯示到網頁的h2標籤上 const h2 = document.getElementById('message'); // 將h2標籤原本的文字替換成message參數拿到的文字 h2.innerText = message; }); </script> </html>

後端 /webapp/java/com/Login.java

package com; import java.io.IOException; import java.io.PrintWriter; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; class ActionResult { private int code = -1; // 動作結過的代碼 private String message = ""; // 動作描述 public ActionResult(int code, String message) { this.code = code; this.message = message; } public int getCode() { return this.code; } public String getMessage() { return this.message; } } public class Login extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { // 接收使用者帳號,參數名稱為username String username = req.getParameter("username"); // 接收使用者密碼,參數名稱為password String password = req.getParameter("password"); // 設定輸出的資料格式和編碼 // resp.setContentType("text/html;charset=UTF-8"); // // 輸出到網頁上 // PrintWriter out = resp.getWriter(); // out.print("收到帳號: " + username); ActionResult actionResult = checkAccount(username, password); // 將網址列的參數值編碼,避免中文字出現亂碼 String url = URLEncoder.encode(actionResult.getMessage(), StandardCharsets.UTF_8.toString()); // 導回前端並將結果透過參數給前端 resp.sendRedirect("/login/loginResult.html?message=" + url); // out.print("<div> Code = " + actionResult.getCode() + "</div>"); // out.print("<div> Message = " + actionResult.getMessage() + "</div>"); // out.print("<br/>"); // out.print("收到密碼: " + password); // 立即輸出 // out.flush(); } // 判斷帳號是否存在 private ActionResult checkAccount(String username, String password) { Connection conn = null; Statement stmt = null; ResultSet rs = null; // 註冊mySQL資料庫驅動程式 try { Class.forName("com.mysql.cj.jdbc.Driver"); // 連線資料庫 conn = DriverManager.getConnection("jdbc:mysql://localhost/shopping?" + "user=root&password=0000"); // 取得Statement物件 stmt = conn.createStatement(); // 查詢該帳號是否存在 rs = stmt.executeQuery("select count(*) as c from account where name='" + username + "'"); // 判斷帳號是否存在 rs.next(); // 跳到查詢結果的第一筆資料 int c = rs.getInt("c"); // 查詢到的資料筆數 // if(c == 0) { // return new ActionResult(2, "帳號不存在"); // } else { // return new ActionResult(0, "成功"); // } // 帳號不存在,直接返回錯誤 if(c == 0) { // 把資料庫相關資源釋放 rs.close(); stmt.close(); conn.close(); return new ActionResult(2, "帳號不存在"); } // 帳號存在,繼續判斷密碼 rs = stmt.executeQuery("select count(*) as c from account " + "where name='" + username + "' and password='" + password + "';"); // 移動到第一筆資料 rs.next(); c = rs.getInt("c"); // 查詢到的資料筆數 // 把資料庫相關資源釋放 rs.close(); stmt.close(); conn.close(); // 密碼錯誤 if(c == 0) { return new ActionResult(3, "密碼錯誤"); } return new ActionResult(0, "登入成功"); // return c == 0 ? new ActionResult(2, "帳號不存在") : new ActionResult(0, "登入成功"); } catch (ClassNotFoundException e) { // 無法註冊(錯誤代碼1) return new ActionResult(1, "無法註冊驅動程式"); } catch (SQLException e) { // SQL操作錯誤(代碼2) return new ActionResult(e.getErrorCode(), e.getMessage()); } } }

/webapp/WEB-INF/web.xml

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>Login</servlet-name> <servlet-class>com.Login</servlet-class> </servlet> <servlet-mapping> <servlet-name>Login</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping> </web-app>

2023-06-27 上課紀錄

檔案下載

Apache Maven: https://maven.apache.org/download.cgi

建立後端開發環境

安裝Maven

  1. 下載後解壓縮到合適的位置
  2. 打開編輯系統的環境變數
  3. 在下方的系统中建立MAVEN_HOME變數,變數值為MAVEN路徑,如 D:\apache-maven-3.9.2-bin\apache-maven-3.9.2
  4. 在上方用户變數中的Path變數内,新增%MAVEN_HOME%\bin
  5. 點擊確定

測試安裝

  1. 打開「命令提示字元」或VSCode終端機。
  2. 輸入mvn -version
  3. 如果能正確顯示版本號碼,則代表安裝成功。

安裝VSCode extension

Community Server Connectors

用途:用來安裝與管理Tomcat後端伺服器用。

Extension Pack for Java

裡面已包含了6個套件:

  • Language Support for Java™ by Red Hat
  • Debugger for Java
  • Test Runner for Java
  • Maven for Java
  • Project Manager for Java
  • Visual Studio IntelliCode

建立VSCode新專案

  1. Ctrl+Shift+P,輸入java,選擇Maven: Create from archetype
  2. 選擇maven-archetype-webapp
  3. 選擇1.4
  4. Group Id直接Enter
  5. artifact Id直接Enter
  6. 選擇一個合適的目錄即可
  7. 然后下方終端機會詢問一些問題,直接Enter兩次即可
  8. 出現綠色的BUILD SUCCESS表示建立成功,可以點右下角藍色按鈕打開project

提示:
如果出現The terminal process failed to launch: Path to shell executable "cmd.exe" does not exist錯誤訊息,請將C:\Windows\System32加到環境變數內的使用者環境變數的Path變數內。

修改pom.xml

1.7修改成1.8

<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties>

新增Servlet套件

  1. Ctrl+shift+P,输入maven,選擇「新增相依套件」
  2. 输入servlet然後enter
  3. 選擇javax.servlet

新增mysql-connector套件

  1. Ctrl+shift+P,输入maven,選擇「新增相依套件」
  2. 输入mysql然後enter
  3. 選擇mysql-connector-java
  1. 有新增相依套件的話,會需要重新編譯maven,此时儲存檔案,右下角會有提示,點及藍色按鈕即可。
  2. 或者可以手動操作,Ctrl+Shift+P,輸入maven,選擇「執行命令」,選擇「compile」,等待終端機出現Build success即可。

安裝Tomcat Server

  1. 在左下角有一些被折疊的欄位,找到SERVERS欄位。
  2. 展開後可以看到Community Server Connector(stopped)
  3. 點擊滑鼠右鍵,選擇出現的的唯一新選項,「開始RSP Provider」,等待變成「Community Server Connector(started)」。
  4. 再次右鍵點擊,選擇建立新伺服器(Create new server)。
  5. Download Server,選擇「Yes」,然後選擇Apache-tomcat-9.0.41
  6. 選擇Yes同意授權

測試Tomcat

  1. 右键Tomcat 9.x(stopped),選擇開始伺服器
  2. 等待變成「Tomcat 9.x(started)」
  3. 完成后,打開瀏覽器,輸入:http://localhost:8080可以看到tomcat網頁。

開發一個Servlet

  1. 在src/main目錄下新增java目錄(Servlet程式碼根目錄)
  2. 新增package目錄com
  3. 新增Servlet檔案Hello.java
package com; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // @WebServlet("/hello") public class Hello extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); // 取得網址上的name參數 String name = request.getParameter("name"); // 取得輸出網頁用的物件 PrintWriter out = response.getWriter(); out.print("<!DOCTYPE html>"); out.print("<html>"); out.print("<head>"); out.print("<title>Hello</title>"); out.print("</head>"); out.print("<body>"); out.printf("<h1>哈囉! %s!\n", name); out.print("</body>"); out.print("</html>"); } }

提示:
課本的第一個Servlet範例

修改src/webapp/WEB-INF目錄下的web.xml

新增servlet設定

<web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>Hello</servlet-name> <servlet-class>com.Hello</servlet-class> </servlet> <servlet-mapping> <servlet-name>Hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> </web-app>

因為書本的@WebServlet在此處無法使用,所以使用web.xml檔來設定Servlet的URL

修改index.jsp

在路徑src\main\webapp\index.jsp中,修改為:

<html> <body> <h2>Hello World!</h2> <a href="HelloServlet">Hello Servlet</a> </body> </html>

部署到Tomcat中

打包專案

  1. Ctrl+Shift+P,輸入maven,選擇「執行命令」,再選擇「package」

加入到Tomcat伺服器中

  1. 右键点击Tomcat 9.x(started),選擇Add Deployment,選擇target\demo.war
  2. 選擇No(不修改参数)
  3. 右键點擊Tomcat 9.x(started),選擇Publish Server(Full)

使用瀏覽器瀏覽Servlet

http://localhost:8080/demo/hello

將今天的Servlet加上連接資料庫

package com; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // @WebServlet("/hello") public class Hello extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); // 取得網址上的name參數 String name = request.getParameter("name"); // 取得輸出網頁用的物件 PrintWriter out = response.getWriter(); out.print("<!DOCTYPE html>"); out.print("<html>"); out.print("<head>"); out.print("<title>Hello</title>"); out.print("</head>"); out.print("<body>"); out.printf("<h1>哈囉哈哈! %s!\n", name); try { out.print("開始讀取資料庫"); getAccountData(out); } catch (Exception e) { // TODO Auto-generated catch block out.print("資料庫讀取錯誤"); } out.print("</body>"); out.print("</html>"); } public void getAccountData(PrintWriter out) { System.out.println("Hello, World!"); Connection conn = null; Statement stmt = null; ResultSet rs = null; try { // 註冊mysql connect 驅動程式 Class.forName("com.mysql.cj.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://localhost/shopping?" + "user=root&password=0000"); stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT * FROM account"); // 移動第一筆資料 while(rs.next()) { // 取得name欄位資料 String name = rs.getString("name"); int age = rs.getInt("age"); // 顯示出來 System.out.printf("name: %s, age: %d\n", name, age); out.printf("<p>name: %s, age: %d</p>", name, age); } // 關閉連線(節省記憶體,釋放網路連線) rs.close(); stmt.close(); conn.close(); out.print("<p>Close</p>"); } catch (SQLException ex) { // handle any errors out.println("<p>SQLException: " + ex.getMessage()); out.println("<p>SQLState: " + ex.getSQLState()); out.println("<p>VendorError: " + ex.getErrorCode()); } catch(ClassNotFoundException e) { out.print("<p>" + e.getMessage() + "</p>"); } } }

2023-06-26 上課紀錄

檔案下載

mySQL: https://dev.mysql.com/downloads/mysql/
mySQL connectors: https://dev.mysql.com/downloads/connector/j/

注意:
請選擇Platform Independent來下載

dbeaver: https://dbeaver.io/download/

安裝VSCode for java

此VSCode版本包含了Java SDK以及所需要的Java extension
https://code.visualstudio.com/docs/languages/java
下載VSCode for Java

關閉VSCode inline code hint

  1. 前往: 檔案->喜好設定->設定
  2. 搜尋hint
  3. 將Editor > Inlay Hints: Enabled的下拉選項改為: off

關閉VSCode checkstyle

  1. 前往: 檔案->喜好設定->設定
  2. 搜尋checkstyle
  3. 將Checkstyle: Enable的Checkbox取消選取

    Controls wheher checkstyle is enabled or not

Markdown介紹

工程師的文件格式,專門用來寫技術文件。

![](https://hojenjen.com/wp-content/uploads/2017/10/1506964794-4efdd2f969559e8b1c92e99f32ded48e.jpg)

- 事項1
- 事項2
- 事項3
---
1. 數字**清單**
2. 數字*清單*
3. 數字清單

|1|2|3|
|-|-|-|
|4|5|6|
|4|5|6|
|4|5|6|

> **注意:**
> 不可以亂丟垃圾
```java=
public class Test {
    public void main(String[] argv) throws Exception {
        System.out.println("Hello Java");
    }
}
\```

使用public語法

```html=
<div>
    test
</div>
\```

最下面一行的最前面的斜線是多餘的,只是為了讓程式碼區塊符號顯示出來

文件

Markdown語法

https://markdown.tw/

SQL connector官方文件

https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-examples.html

SQL語法

https://blog.techbridge.cc/2020/02/09/sql-basic-tutorial/

修改root密碼的SQL語法

ALTER USER 'root'@'localhost' IDENTIFIED BY '新密碼';

注意:

  1. 新密碼欄位請改成你的密碼
  2. 因為透過SQL connector連線的時候,密碼有特殊字元會造成連線失敗,所以修改密碼

透過Java程式來連接資料庫並Query資料

建立專案

  1. Ctrl+Shift+P,輸入java,選擇: Create Java Project
  2. 選擇No Build Tool
  3. 輸入專案名稱

    這也會是你的程式碼的目錄名稱

完整程式碼

import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class App { public static void main(String[] args) throws Exception { System.out.println("Hello, World!"); Connection conn = null; Statement stmt = null; ResultSet rs = null; try { conn = DriverManager.getConnection("jdbc:mysql://localhost/shopping?" + "user=root&password=0000"); stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT * FROM account"); // 移動第一筆資料 while(rs.next()) { // 取得name欄位資料 String name = rs.getString("name"); int age = rs.getInt("age"); // 顯示出來 System.out.printf("name: %s, age: %d\n", name, age); } // 關閉連線(節省記憶體,釋放網路連線) rs.close(); stmt.close(); conn.close(); } catch (SQLException ex) { // handle any errors System.out.println("SQLException: " + ex.getMessage()); System.out.println("SQLState: " + ex.getSQLState()); System.out.println("VendorError: " + ex.getErrorCode()); } } }

分組

4~6人一組

角色

  • 前端 - 負責網頁端開發
  • 後端 - 負責API與資料庫
  • 專案管理與UI/UX - 負責介面、進度與規格制定

補充:
負責前端開發的學員並不是只能做前端,而是負責前端的整合與進度追蹤;同理,專案管理的學員也可以進行前後端開發,但會額外負責介面的發想與整體專案進度的掌控。

組別 前端 後端 專案管理 其它
第一組
第二組
第三組
第四組

多型範例

import java.util.ArrayList; abstract class Shape { public abstract void show(); public abstract void jump(); } class Triangle extends Shape { public void show() { System.out.println(" * "); System.out.println(" *** "); System.out.println("*****"); } public void jump() { } } class Rectangle extends Shape { public void show() { System.out.println("*****"); System.out.println("*****"); System.out.println("*****"); } public void jump() { } } class RTriangle extends Shape { public void show() { System.out.println("*****"); System.out.println(" *** "); System.out.println(" * "); } public void jump() { } } class RTriangle2 extends Shape { public void show() { System.out.println("*****"); System.out.println(" *** "); System.out.println(" * "); } public void jump() { } } public class App { public static int a = 0; public int b = 0; public static void main(String[] args) throws Exception { // Triangle triangle = new Triangle(); // triangle.show(); // Rectangle rectangle = new Rectangle(); // rectangle.show(); // RTriangle rTriangle = new RTriangle(); // rTriangle.show(); // RTriangle2 rTriangle2 = new RTriangle2(); // rTriangle2.show(); ArrayList<Shape> shapes = new ArrayList<>(); shapes.add(new Triangle()); shapes.add(new Rectangle()); shapes.add(new RTriangle()); shapes.add(new RTriangle2()); for(Shape s : shapes) { s.show(); s.jump(); } } }

學生筆記

# print('hello python') # print('hello python',2021) # print('hello python','Today is a sunny day') # print(3+3) # print(123,345, sep='-') # print(type(1)) # b = input() # print(b) # b = input('請輸入您的名字:') # print() # a = input('請輸入一個數字:') # b = input('請再輸入一個數字') # print('這是你剛剛輸入的資料', a, '和', b) # a = input('請輸入一個數字:') # b = input('請再輸入一個數字') # c = int(a) # d = int(b) # print('這是你剛剛輸入的資料', a, '和', b) # print('a變數資料的型態是', type(a)) #標準輸入的資料型態永遠是字串型態 # print('c變數資料的型態是', type(c)) # print('a+b 加總', a+b) # print('c+d 加總', c+d) # # print('a和b 相減', a-b) 會出現錯誤 # print('c和d 相減', c-d) # print('a和b 相乘', c*d) # print('a和b 相除', c/d) # a = input('請輸入一個數字:') # b = input('請再輸入一個數字') # c = float(a) # d = float(b) # print('這是你剛剛輸入的資料', a, '和', b) # print('a變數資料的型態是', type(a)) #標準輸入的資料型態永遠是字串型態 # print('c變數資料的型態是', type(c)) # print('c+d 加總', c+d) # print('c和d 相減', c-d) # import decimal # a1 = decimal.Decimal('1') # a2 = decimal.Decimal('0.8') # print('另一種減法運算:', a1 - a2) # #算術運算子 # print(3+3) # print(4-1) # print(5*2) # print(6/3) # print(2**5) #指數的意思,次方 # print(17//4) #整數除 # print(17%4) #取餘數 # print('abc'*3) #字串有支援乘法 # print('abc'+ 'cde') # 布林型態 # print(type(True)) # print(type(False)) # print(3>4) # print(9<10) # print(3>=3) # print(6<=5) # print(9==8) #等於 # print(8!=7)#不等於 # a=3 # b=4 # if a>b: # print('ok') # 條件式結果為True執行這行 # else: # print('ko') # 條件式結果為False執行這行 # import random #模組 module # a = random.randint(1,9) #1到9之間隨機選一個數字 # print(a) # import random # answer = random.randint(1,10) # print(answer) # player = input('猜一個1~10之間的數字:') # player = int(player) # if answer == player: #記得要打冒號':' # print('恭喜你答對了') # else: # print('答錯了哈哈', '答案是:', answer) #List群集資料型態,只能放數字,取資料 # a = [35,63,77,66,75,92,66,87] # print(type(a)) # print(a) # print(a[1]) #索引從0開始算, 0=35, 1=63 # print(a[4]) # print(a[-1]) #從後面開始算 # print(a[-3]) # print(a[1:3]) #1=start,:=範圍,3=結束但不包含3這個數字 # print(a[2:5]) # print(a[-3:-1]) # print(a[1:4:2]) #1=start起, 4=end終, 2=step跳 # print(a[:4]) #若:前面沒有數字,數字預設就是0 # print(a[:]) # print(a[::-1]) #拿資料的方向相反 # print(a[-3:-6:-1]) # print(a[-1:-3]) #沒有結果,因為方向預設是1,往右邊所以取不到數字 # #索引運算 # a = [35, 63, 77, 66, 75, 92, 67, 87] # print(a) # print(type(a)) # print(a[1:3]) # print(a[2:6:2]) # print(a[:]) # print(a[::]) # print(a[::-1]) # print(a[-1:-3]) # print(a[-3:-1]) # print(a[-1:-3:-1]) # print(a[-3:-1:-1]) # #迴圈For, 取代了上方的索引運算 # a = [35,63,77,66,75,92,66,87] # for x in a: # print(x*2) #例子:做一個判斷奇偶數的小遊戲 # user1 = int(input('請輸入一個數字')) # print(user1) # if user1%2 == 1: # print('此數字為奇數') # else: # print('此數字為偶數') #練習1:寫一個程式將上面list裡面的奇數顯示在書面上 # a=[35,63,77,82,95,24,60] # for x in a: # if x % 2 == 1: # print('奇數為',x)
import requests import csv url = 'http://data.tycg.gov.tw/api/v1/rest/datastore/a1b4714b-3b75-4ff8-a8f2-cc377e4eaa0f?format=csv' response = requests.get(url) #print(response.text) rows = response.text.splitlines() user = input('請輸入要搜尋的站台部分名稱:') rowdata = list(csv.reader(rows)) for row in rowdata: if user in row[3]: print('站名:', row[3], ', 地址:', row[6]) print(' - 可借:', row[12]) print(' - 可還:', row[5]) print(' - 總數:', row[10]) print()