1. Spring Initializr
2. 项目结构
<package>java.com.kenny.ai | desciption |
config | 配置类 |
controller | 控制器 |
entity | 实体类 |
intercept | 拦截器 |
mapper | MyBatis映射类 |
service | 服务类 |
utils | 工具类 |
<package> resources | desciption |
mapper | MyBatis XML映射类 |
static | js, css, images |
templates | thymeleaf模板 |
application.properties | springboot xml配置 |
jdbc.properties | mysql 配置 |
schema.sql | sql 初始化 |
3. Starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
</dependencies>
<pom.xml> starter | desciption |
spring-boot-starter-web | Spring框架 |
spring-boot-starter-test | / |
spring-boot-starter-thymeleaf | 渲染模板 |
mysql-connector-java | mysql |
mybatis-spring-boot-starter | mybatis持久层 |
4. xml配置
AiApplication.java
package com.kenny.ai; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.PropertySource; @SpringBootApplication @ComponentScan(basePackages = {"com.kenny.ai.*"}) @MapperScan(basePackages = {"com.kenny.ai.mapper"}) @PropertySource(value={"classpath:jdbc.properties"}, ignoreResourceNotFound=true) public class AiApplication { public static void main(String[] args) { SpringApplication.run(AiApplication.class, args); } }
Label | Description |
@SpringBootApplication | Spring框架 |
@ComponentScan | 扫描根目录 |
@MapperScan | Mybatis映射层 |
@PropertySource | 数据库xml配置 |
application.properties
server.port=8080 mybatis.mapper-locations=classpath:mapper/*.xml
jdbc.properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/spring_boot?useSSL=false&useUnicode=true&characterEncoding=UTF-8 spring.datasource.username=user002 spring.datasource.password=123456 spring.datasource.sql-script-encoding=utf-8 spring.datasource.initialization-mode=always
- 项目端囗配置
- mybatis映射xml
- mysql配置
重点:mysql必须有useUnicode=true&characterEncoding=UTF-8,否则会编码错误。
5. mybatis持久层
spring.datasource.initialization-mode=always,这句会自动扫描resources/schema.sql。
5.1 schema.sql
DROP TABLE IF EXISTS `stock`; CREATE TABLE stock ( id INT PRIMARY KEY auto_increment, ucode VARCHAR(256) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, uname_tc VARCHAR(512) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, uname_en VARCHAR(512) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 DEFAULT COLLATE utf8_general_ci; INSERT INTO `stock`(`id`,`ucode`,`uname_tc`,`uname_en`) values (1,'0700','騰訊控股','Tencent Holdings Ltd.'), (2,'1810','小米集團','Xiaomi Singapore Pte. Ltd.'), (3,'3690','美團','Meituan Corp'), (4,'9988','阿里巴巴','Alibaba Group'), (5,'2800','盈富基金','Tracker Fund of Hong Kong'), (6,'3033','南方東英恒生科技指數ETF','CSOP Hang Seng TECH Index ETF HKD');
5.2 java.com.kenny.ai.entity.Stock.java 实体类
package com.kenny.ai.entity; import org.springframework.stereotype.Component; @Component("stock") public class Stock { private Long id; private String ucode; private String uname_tc; private String uname_en; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUcode() { return ucode; } public void setUcode(String ucode) { this.ucode = ucode; } public String getUname_tc() { return uname_tc; } public void setUname_tc(String uname_tc) { this.uname_tc = uname_tc; } public String getUname_en() { return uname_en; } public void setUname_en(String uname_en) { this.uname_en = uname_en; } @Override public String toString() { return "Stock{" + "id=" + id + ", ucode='" + ucode + '\'' + ", uname_tc='" + uname_tc + '\'' + ", uname_en='" + uname_en + '\'' + '}'; } }
5.3 java.com.kenny.ai.mapper.StockMapper.java
mapper映射层,注入Stock实体类。实现增删改查。
package com.kenny.ai.mapper; import com.kenny.ai.entity.Stock; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.springframework.stereotype.Component; import java.util.List; @Mapper @Component public interface StockMapper { /*** * 查詢所有正股 * @return 正股列表 */ @Select({"select id, ucode, uname_tc, uname_en from stock"}) List<Stock> listAll(); /*** * 根據ucode查詢正股 * @param ucode 正股編號 * @return 当前ucode的正股,不存在则是 {@code null} */ @Select({"SELECT id, ucode, uname_tc, uname_en from stock WHERE ucode = #{ucode}"}) Stock findByUcode(@Param("ucode") String ucode); /*** * 保存正股 * @param stock 正股 * @return 成功 - {@code 1} 失败 - {@code 0} */ int saveStock(@Param("stock") Stock stock); /*** * 更新正股 * @param stock 正股 * @return 成功 - {@code 1} 失败 - {@code 0} */ int updateStock(@Param("stock") Stock stock); /*** * 删除正股 * @param ucode 正股編號 * @return 成功 - {@code 1} 失败 - {@code 0} */ int deleteByUcode(@Param("ucode") String ucode); }
5.4 resources.mapper.StockMapper.xml
mapper映射层的xml配置。配置了较复杂的sql stmt。
重点:路径在application.properties配置。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.kenny.ai.mapper.StockMapper"> <insert id="saveStock"> INSERT INTO `stock` (`ucode`, `uname_tc`, `uname_en`) VALUES (#{stock.ucode}, #{stock.uname_tc}, #{stock.uname_en}) </insert> <update id="updateStock"> UPDATE `stock` SET uname_tc=#{stock.uname_tc}, uname_en=#{stock.uname_en} WHERE ucode=#{stock.ucode} </update> <delete id="deleteByUcode"> DELETE FROM `stock` WHERE `ucode` = #{ucode} </delete> </mapper>
6. Controller控制器
注入Stock实体类 和 StockMapper映射类。因业务简单,所以暂时地不用Service层。
默认使用thymeleaf模板渲染。
6.1 com.kenny.ai.controller.StockController.java
package com.kenny.ai.controller; import com.kenny.ai.entity.Stock; import com.kenny.ai.mapper.StockMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class StockController { @Autowired private final StockMapper stockMapper; public StockController(StockMapper stockMapper) { this.stockMapper = stockMapper; } @RequestMapping(value={"/", "/index", "/home", "/uat/v2/home"}) public String index(Model model) { model.addAttribute("stocks", this.stockMapper.listAll()); return "stock/stock"; } @RequestMapping({"stock/delete/{ucode}", "uat/v2/stock/delete/{ucode}"}) public String deleteStock(@PathVariable String ucode) { stockMapper.deleteByUcode(ucode); return "redirect:/home"; } @RequestMapping({"stock/save", "uat/v2/stock/save"}) public String stockSave(Stock stock) { stockMapper.saveStock(stock); return "redirect:/home"; } @RequestMapping({"stock/update", "uat/v2/stock/update"}) public String stockUpdate(Stock stock) { stockMapper.updateStock(stock); return "redirect:/home"; } }
7. Thymeleaf模板
7.1 webapp结构
spring-boot强烈推荐thymeleaf完全取代jsp,那就试用thymeleaf吧。
7.2 最终效果
7.3 resources/templates/fragments/header.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head th:fragment="common_header(title)"> <title th:replace="${title}"></title> <meta charset="utf-8"></meta> <meta name="viewport" content="width=device-width, initial-scale=1.0"></meta> <script th:src="@{/js/jquery.min.js}" type="text/javascript"></script> <script th:src="@{/js/bootstrap.min.js}" type="text/javascript"></script> <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet" /> <link th:href="@{/css/kuang-style.css}" rel="stylesheet" /> <link th:href="@{/css/style.css}" rel="stylesheet" /> </head>
th:fragment="common_header(title)"
fragment名称:common_header
注入叁数:title
重点:@{/} 指向根目录。
<head th:replace="fragments/header :: common_header(~{::title})">
7.4 resources/templates/stock/stock.html
7.4.1 渲染表格
<tr th:if="${stocks==null or stocks.isEmpty()}"> <td colspan="4"> 沒有合適的正股 </td> </tr> <tr th:each="stock : ${stocks}"> <form th:action="@{/stock/update}" method="post"> <td><span th:text="${stock.ucode}"> </span></td> <td><input type="text" class="form-control" name="uname_tc" th:value="${stock.uname_tc}"> </input></td> <td><input type="text" class="form-control" name="uname_en" th:value="${stock.uname_en}"> </input></td> <td> <button type="submit" class="btn btn-sm btn-primary btn-update">更新</button> <input type="hidden" class="form-control" name="ucode" th:value="${stock.ucode}"> </input> <button type="button" th:href="@{'/stock/delete/'+${stock.ucode}}" class="btn btn-sm btn-danger btn-delete">刪除</button> </td> </form> </tr>
7.4.2 新增数据
name=ucode
name=uname_tc
name=uname_en
会自动映射至Stock实体,并经StockMapper映射,进行数据库事务。
<form th:action="@{/stock/save}" method="post"> <div class="form-row"> <div class="form-group col-12 col-md-4"> <label for="ucode">正股</label> <input type="text" class="form-control" id="ucode" name="ucode" placeholder="0700" required /> </div> <div class="form-group col-12 col-md-4"> <label for="uname_tc">中文</label> <input type="text" class="form-control" id="uname_tc" name="uname_tc" placeholder="騰訊控股" required /> </div> <div class="form-group col-12 col-md-4"> <label for="uname_en">英文</label> <input type="text" class="form-control" id="uname_en" name="uname_en" placeholder="Tencent Holdings Ltd." required /> </div> <button type="submit" class="btn btn-sm btn-primary" id="submit">Submit</button> </div> </form>