2021年1月31日 星期日

[SpringBoot 1.5] 錯誤處理 @ControllerAdvice, @ExceptionHandler, @ReponseStatus

 SpringBoot有預設的錯誤頁面處理

要實現這個我們首先可以在resource>templates>error的資料夾下

加入兩個頁面500.html , 404.html

並在resource>templates下面放index.html

然後造一個controller

@Controller
public class IndexController {

@GetMapping("/")
public String index(){
int i = 9/0;
return "index";
}
}

由於9/0會報錯

畫面就會自動導到命名為500的頁面

將報錯的那一行註解掉

我們在Build中點Recompile

然後刷新頁面

畫面就會來到首頁了

而當我們的URL打一個沒有資源的

ex http://127.0.0.1:8081/123456

他就會導到404的頁面

------------------------------------------------

而~如果我們要使用錯誤頁面, 並用其來顯示錯誤訊息, 可以以下搭配

1 先在error資料夾下產生一個error.xml檔案如下

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<html lang="en">
<head>
<meta charset="UTF-8">
<title>錯誤</title>
</head>
<body>
<h1>錯誤</h1>

<div>
<div th:utext="'&lt;!--'" th:remove="tag"></div>
<div th:utext="'Failed Request URL : ' + ${url}" th:remove="tag"></div>
<div th:utext="'Exception message : ' + ${exception.message}" th:remove="tag"></div>
<ul th:remove="tag">
<li th:each="st : ${exception.stackTrace}" th:remove="tag"><span th:utext="${st}" th:remove="tag"></span></li>
</ul>
<div th:utext="'--&gt;'" th:remove="tag"></div>
</div>

</body>
</html>

2產生一個掛@ControllerAdvice的類別

他主要用法是一個搭配ExceptionHandler攔截Exception的一個功能

下列程式碼, 基本上攔截後, 如果他有掛ResponseStatus類別的話, 他會繼續throw e

而如果沒有掛ResponseStatus就會回傳一個ModelAndView物件並導到error頁面

@ControllerAdvice
public class ControllerExceptionHandler {

private final Logger logger = LoggerFactory.getLogger(this.getClass());
@ExceptionHandler(Exception.class)
public ModelAndView exceptionHandler(HttpServletRequest request, Exception e) throws Exception {
logger.error("Request URL : {}, Exception : {}",request.getRequestURL(),e);

if(AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class)!=null){
throw e;
}

ModelAndView mv= new ModelAndView();
mv.addObject("url",request.getRequestURL());
mv.addObject("exception",e);
mv.setViewName("error/error");
return mv;
}
}


而, 如果我們想要做出對應404的Exception

1 產生一個個性化的Exception類別並掛上ResponseStatus

@ResponseStatus(HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException{
public NotFoundException() {
}

public NotFoundException(String message) {
super(message);
}

public NotFoundException(String message, Throwable cause) {
super(message, cause);
}
}


2在indexController中加入以下這段

String blog=null;
if(blog==null){
throw new NotFoundException("博客不存在");
}

因為剛剛已經在ControllerExceptionHandler中設置要繼續拋出e了

他就會跳轉到404的頁面



2021年1月30日 星期六

MySQL安裝問題

 一開始在安裝的時候, 如果一直點下一步的話

可能會遇到沒有安裝server的問題

事實上比較主要的部份包括介面, 還有server







所以, 如果裝完之後, 發現少了什麼, 要回來installer檢查一下

2021年1月26日 星期二

20210126月會 工作啟發

因為我們每兩周都有(同仁)分享報告

而主管也有要求我們要回饋分享的報告內容

主管也會統計這些回饋分配

由這樣的回饋我觀察有以下幫助

1 訓練對於簡報的報告品質(因為在回饋的同時也會注意到自己對報告的要求)

2 因為有一次一次的回饋, 同仁可以透過別人的回饋而改善下一次的報告品質

3 幫助同仁有互相幫助和欣賞的機會

4 由於差不多一兩個月就需要分享一次, 需要平時就累積工作上的職能(個人職能提升)

5 對於在場的聽者, 可以聽到不同與職能相關項目, 有助於開拓視野


2021年1月22日 星期五

寫完20篇心情和前輩交流紀錄

從12月至今, 已經完成了20篇文章,
這段時間, 考了兩次多益, 結果同分, 閱讀還略退步了點,
托福講師說, 還是從閱讀中來學單字, 會比單獨背單字會有效一點
這些也是題外話, 這是寫到一半, 大約斷了兩周的原因, 
然後~之後因為會準備考托福的緣故, 幾乎整周的時間表都確定
4天補英文, 3天+1天(不固定)會舉行英文讀書會, 在民權的路易莎

這段期間小有收穫, 幾次下來讀書會總成員已經有5位, 1女4男
有特別挑過是真心想讀書(把愛拉賽的都挑掉), 都可以坐上兩個小時,
能一起讀書, 的確比之前自己拚的感覺好, 一個人的進修是真的很煎熬,
特別取了名字Elite Programmer Taipei, 這是我對於自己的期許, 
我希望自己能成為Programmer中的菁英

來記錄一下想寫網誌的心情,
原本一開始, 是因為在工作上覺得有些東西, 受了前輩很大的幫助,
如果紀錄下來, 我覺得這些對於我在工作上會很好,
後來有一天, 我看到保哥的網誌, 我注意到了他年月出的文章,
剛開始的兩年, 幾乎每個月能出32篇文章,
他是.net領域的神級人物, 如果說, 這是他的變強途徑,
那麼我能不能複製呢?
想到這裡, 我就開始複製同樣的想法, 我也要每個月寫個30篇

關於內容上, 目前是以看Utube教學, 邊做, 邊寫筆記,
這一部分還是比較屬於開專案所要使用到的基本技能,
重點來了~~~~
---------------------------------------------------------------------------
今天讀書會的前輩跟我講說
其實這些spring的各項目IOC,AOP, JPA, SpringCloud
都是一個一個主題, 
一開始可以粗略地去懂怎麼使用就好,
但是更進階的, 可以去了解它的原碼,
懂得其運作的緣由, 進一步就可以對其設定進行操作,
有些JVM的調教, 提升效率的方式, 還是要對其原碼懂得操作,
才能夠設計得到位,
除此之外, 可以在每個新版本推出來的項目上,
比較其和舊版本中改善的項目, 或是新出現的項目,
那可能解決的問題, 就是目前該領域中最Hot最前衛的技術
研究其技術, 就自然會在該領域中作為技術的領導角色

聽完這些話真是價值連城阿,
一直在想, 為什麼公司裡屌不拉基的前輩為什麼可以這麼厲害,
在讀書會中, 聽到讀書會的前輩分享這些他關注的事情,
讓我體會出, 到底要如何才能更往高的階段去
---------------------------------------------------------------------------
目前一切都覺得進行不錯,
有穩定的英文和程式時間, 
擁有英文讀書會和程式讀書會各一, 都為主招集人,
幾乎每天寫技術文章, 有時一天還出兩篇
運動時間兩天, 相親時間一晚 也慢慢排入,
而最後大概就一天晚上玩遊戲,
我想, 成為大神的路慢慢的, 不會是夢想
現在比較主要的, 累積一些後, 跳到更厲害的地方去,
這樣就能跟更厲害的人切磋學習,

透過觀察大神所走過的路徑, 我相信我能更快的完成夢想


[SpringBoot 1.5] SpringBoot 新增範例

我們說到新增功能

他應該要包含兩個步驟

1 點進去進入到一個可以填寫空白資訊的頁面(employee>Get)

2 輸入完新增的資訊然後按下儲存鍵, 完成新增項目的儲存(employee>Post)

從畫面過來, 一開始是點擊這個標籤

<a href="emp" th:href="@{/emp}" class="btn btn-sm btn-success">員工添加</a>

然後會進到controller, 因為裡面會要將所有可以選取的Department顯示出來,

所以要先getDepartments再放到model中

@GetMapping("/emp")
public String toAddPage(Model model){
// 來到添加頁面, 查出所有的部門, 在頁面顯示
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("depts",departments);
return "emp/add";
}

這裡的Department類別

public class Department {

private Integer id;
private String departmentName;
}

這是Dao

@Repository
public class DepartmentDao {

private static Map<Integer, Department> departments = null;

static{
departments = new HashMap<Integer, Department>();

departments.put(101, new Department(101, "D-AA"));
departments.put(102, new Department(102, "D-BB"));
departments.put(103, new Department(103, "D-CC"));
departments.put(104, new Department(104, "D-DD"));
departments.put(105, new Department(105, "D-EE"));
}

public Collection<Department> getDepartments(){
return departments.values();
}

public Department getDepartment(Integer id){
return departments.get(id);
}

}

然後畫面上的form是這樣

<form th:action="@{/emp}" method="post">

<input type="hidden" name="_method" value="put" th:if="${emp != null}">
<
input type="hidden" name="id" th:value="${emp != null} ? ${emp.id}">
<
div class="form-group">
<
label>LastName</label>
<
input name="lastName" type="text" class="form-control" placeholder="zhangsan" th:value="${emp != null} ? ${emp.lastName}">
</
div>
<
div class="form-group">
<
label>Email</label>
<
input name="email" type="email" class="form-control" placeholder="zhangsan@atguigu.com" th:value="${emp != null} ? ${emp.email}">
</
div>
<
div class="form-group">
<
label>Gender</label><br/>
<
div class="form-check form-check-inline">
<
input class="form-check-input" type="radio" name="gender" value="1" th:checked="${emp != null} ? ${emp.gender == 1}">
<
label class="form-check-label"></label>
</
div>
<
div class="form-check form-check-inline">
<
input class="form-check-input" type="radio" name="gender" value="0" th:checked="${emp != null} ? ${emp.gender == 0}">
<
label class="form-check-label"></label>
</
div>
</
div>
<
div class="form-group">
<
label>department</label>
<
select class="form-control" name="department.id">
<option th:value="${dept.id}" th:each="dept : ${depts}" th:text="${dept.departmentName}" th:selected="${emp != null} ? ${dept.id == emp.department.id}"></option>
</
select>
</
div>
<
div class="form-group">
<
label>Birth</label>
<
input name="birth" type="text" class="form-control" placeholder="zhangsan" th:value="${emp != null} ? ${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm:ss')}">
</
div>
<
button type="submit" class="btn btn-primary" th:text="${emp != null} ? '修改' : '添加'"></button>
</
form>

其中我們可以看到拿出depts塞到dept中, 他會需要將id塞到value中, 要特別注意一下













那麼他送出, 就是一個Employee的model

這樣畫面上是使用name來做對應屬性

而department則要用department.id來對應

這個form發出的是Post請求

@PostMapping("/emp")
public String addEmp(Employee employee){
System.out.println("保存的員工信息: "+employee);
employeeDao.save(employee);
return "redirect:/emps";
}

但是記得進入的時候要重新導向redirect

而以下是dao的 save功能

private static Integer initId = 1006;
public void save(Employee employee){
if(employee.getId() == null){
employee.setId(initId++);
}

employee.setDepartment(departmentDao.getDepartment(employee.getDepartment().getId()));
employees.put(employee.getId(), employee);
}

還有一個日期要做補充

通常預設日期會為20xx/xx/xx 

如果要格式化日期, 是可以在application.properties中做控制的, 如以下

spring.mvc.date-format=yyyy-mm-dd


[SpringBoot 1.5] SpringBoot 查詢範例

當我們要將員工資訊顯示出來

這就是一個查詢功能







我們的model為一個Employee類別 屬性如下

public class Employee {

private Integer id;
private String lastName;

private String email;
//1 male, 0 female
private Integer gender;
private Department department;
private Date birth;

然後我們設置一個controller中的方法

@Repository
public class EmployeeDao {
@GetMapping("/emps")
public String list(Model model){
Collection<Employee> employees = employeeDao.getAll();

model.addAttribute("emps",employees);
return "emp/list";
}

我們使用model.addAttribute來設置model中內容

當他點選到該連結, 就可以導向過來controller再return給list頁面

<a class="nav-link active"
th:class="${activeUri=='emps.html'?'nav-link active':'nav-link'}"
href="#" th:href="@{/emps}">

其中搭配著employeeDao為

@Repository
public class EmployeeDao {
private static Map<Integer, Employee> employees = null;

@Autowired
private DepartmentDao departmentDao;

static{
employees = new HashMap<Integer, Employee>();

employees.put(1001, new Employee(1001, "E-AA", "aa@163.com", 1, new Department(101, "D-AA")));
employees.put(1002, new Employee(1002, "E-BB", "bb@163.com", 1, new Department(102, "D-BB")));
employees.put(1003, new Employee(1003, "E-CC", "cc@163.com", 0, new Department(103, "D-CC")));
employees.put(1004, new Employee(1004, "E-DD", "dd@163.com", 0, new Department(104, "D-DD")));
employees.put(1005, new Employee(1005, "E-EE", "ee@163.com", 1, new Department(105, "D-EE")));
}

private static Integer initId = 1006;


public Collection<Employee> getAll(){
return employees.values();
}
}

而前端的頁面為

<tr th:each="emp:${emps}">
<td th:text="${emp.id}"></td>
<td>[[${emp.lastName}]]</td>
<td th:text="${emp.email}"></td>
<td th:text="${emp.gender} == 0 ? '' : ''"></td>
<td th:text="${emp.department.departmentName}"></td>
<td th:text="${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm:ss')}"></td>
<td>
<!--<a th:href="@{/emp/} + ${emp.id}" class="btn btn-sm btn-primary">编辑</a>-->
<a th:href="@{/emp/} + ${emp.id}" class="btn btn-sm btn-primary">编辑</a>
<button th:attr="del_uri = @{/emp/} + ${emp.id}" type="submit" class="btn btn-sm btn-danger deleteBtn">删除</button>
</td>
</tr>

為一個使用th:each來遍歷model中emps的每一筆資料, 時間格是可以用dates.format

查出每筆資料有兩種寫法, 一種為th:text="${model.attribute}"

另外一種為[[${model.attribute}]]


[SpringBoot 1.5] Thymeleaf 判斷顯示根據參數


 











我們在左邊的目錄, 在情境下會想要當點選的時候, 要改變不同的顏色

可以用以下的判斷來顯示

在list.html中使用以下

<div th:replace="commons/bar::#sidebar(activeUri='emps.html')"></div>

用以告訴對方目前頁面是emps.html

而dashboard.html中使用以下

<div th:replace="commons/bar::#sidebar(activeUri='main.html')"></div>

用以告訴對方目前頁面是main.html

而在被鑲嵌的目錄就可以設置以下

<a class="nav-link active"
th:class="${activeUri=='main.html'?'nav-link active':'nav-link'}"
href="#" th:href="@{main.html}">

這個就是判斷如果現在點選該頁面的話, 目前的activeUri被設置為main.html就會判斷出來

然後因此nav-link的css值就會有active

同理, 如果是Customers被選到的時候要讓他有顯示

就用以下這段就行了

<a class="nav-link active"
th:class="${activeUri=='emps.html'?'nav-link active':'nav-link'}"
href="#" th:href="@{/emps}">


Aider及gemini合併使用心得

嘗試Aider SDD套用gemini 我發現ai全自動產code有一個現象, 就是我心中有一個完美的想法, ai可能基於他的背景資訊未必想得到, 也或著就是ai已經被各種知識訓練過, 他可能選擇了一種, 但是並沒有查覺到其中有什麼問題, 很多時候的solution就是想得太簡單...