您好,登錄后才能下訂單哦!
這篇文章主要介紹“總結從單體架構到分布式數據持久化,ORM框架之Mybatis”,在日常操作中,相信很多人在總結從單體架構到分布式數據持久化,ORM框架之Mybatis問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”總結從單體架構到分布式數據持久化,ORM框架之Mybatis”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
1前置概念
01 持久化
持久化就是把數據保存到可以永久保存的存儲設備中,比如磁盤。
02 JDBC
大多數程序員在學習 Java 的過程中,當學習到 Java 訪問數據庫的時候,一定會先學習 JDBC,它是一種用于執行 SQL 語句的 Java API,為數據庫提供統一訪問,并把數據“持久化”到數據庫中。
我再通俗地解釋一下(對 JDBC 有一定了解的同學可以直接跳過):
Sun 公司在 97 年發布 JDK1.1 ,JDBC 就是這個版本中一個重要的技術點,要用 Java 語言連接數據庫,正常的思維都是 Sun 公司自己來實現如何連接數據庫、如果執行 SQL 語句,但是市場上的數據庫太多了,數據庫之間的差異也很大,而且 Sun 公司也不可能了解每個數據庫的內部細節吶...
于是為了讓 Java 代碼能更好地與數據庫連接,Sun 公司于是制定了一系列的接口,說是接口,其實也就是一套【標準】、一套【規范】,具體代碼如何實現由各個數據庫廠商來敲代碼;所以我們常說的“驅動類”,就是各個廠商的實現類。
所以我們在用 JDBC 連接數據庫的時候,第一步需要注冊驅動,就是要告訴 JVM 使用的是操作哪個數據庫的實現類。
03 ORM
在沒有 ORM 框架之前,我們操作數據庫需要這樣:
我們可以看到使用 JDBC 操作數據庫,代碼比較繁瑣,參數拼寫在 SQL 中容易出錯,而且可讀性比較差,增加了代碼維護的難度。
有了 ORM 框架之后,我們操作數據庫是這樣的:
ORM 框架在 Java 對象和數據庫表之間做了一個映射,封裝了數據庫的訪問細節,我們再需要操作數據庫語句的時候,直接操作 Java 對象就可以了。
2Spring Boot 集成 MyBatis
Java 常用的 ORM 框架有 Hibernate、MyBatis、JPA 等等,我在后文中在比較這集中框架的優缺點,本章節主要介紹 Spring Boot 項目集成 MyBatis 訪問數據庫。
Step 1. 添加依賴
<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.0.1</version> </dependency>
Step 2. 配置數據庫鏈接
在 application.yml 文件中配置數據庫相關信息。
#數據源配置 spring: datasource: #數據庫驅動 driver-class-name: com.mysql.cj.jdbc.Driver #數據庫 url url: jdbc:mysql://127.0.0.1:3306/arch?characterEncoding=UTF-8&serverTimezone=UTC #用戶名 username: root #密碼 password: root
Step 3. 配置數據庫鏈接
在我們本機的數據庫上,創建一個用戶表,并插入一條數據:
CREATE TABLE IF NOT EXISTS `user`( `id` INT UNSIGNED AUTO_INCREMENT, `userid` VARCHAR(100) NOT NULL, `username` VARCHAR(100) NOT NULL, `gender` CHAR(1) NOT NULL, `age` INT NOT NULL, PRIMARY KEY ( `id` ) )ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into user(userid ,username, gender, age) values('dashu','大叔','M',18);
Step 4. 創建 Model 層
通常用于接收數據庫中數據的對象,我會單獨創建一個 model package,類中的屬性我習慣和字段保持相同。
package com.archevolution.chapter4.model; public class User { private int id;//主鍵ID,自增長 private String userId;//用戶ID,或看做登錄名 private String userName;//用戶姓名 private String gender;//性別 private int age;//年齡 //省略 get、set、toString 方法 }
Step 5. 創建 Dao 層
通常直接和數據庫打交道的代碼,我們把它們放在 DAO 層(Data Access Object),數據訪問邏輯全都在這里;
我們新建一個 dao package,并在下面新建一個**接口**,注意是接口:
@Mapper public interface UserDao { @Select("SELECT id, userId, userName, gender, age FROM USER WHERE id = #{id}") public User queryUserById(@Param("id") int id); }
這里我多說幾句!
從技術角度來說,model 中的屬性可以和表中的字段不一樣,比如我們在數據庫中增加一個手機號的字段叫做 [mobilephone] :
--增加手機號字段 ALTER TABLE user ADD mobilephone varchar(15) ; --更新 userid = 1 數據的手機號 update user set mobilephone = '13800000000' where userid = '1';
我們在 User.java 中添加一個字段,叫做 [telephone]:
我們自己知道 [telephone] 是和數據庫中的 [mobilephone] 對應,但是如何讓 Mybatis 知道這兩個字段要對應上呢?有幾個辦法:
01. 在 SQL 語句中控制,對名字不相同的字段起別名:
@Select("SELECT id, userId, userName, gender, age, mobilephone as telephone FROM USER WHERE id = #{id}") public User queryUserTelById(@Param("id") int id);
02. 使用 @Results 標簽,將屬性和字段不相同的設置映射(名稱相同的可以不寫):
@Select("SELECT id, userId, userName, gender, age, mobilephone FROM USER WHERE id = #{id}") @Results({ @Result(property = "telephone" , column = "mobilephone") }) public User queryUserTelById2(@Param("id") int id);
不過我還是建議大家在寫 model 類的時候,屬性和表中的字段保持一模一樣,這樣不僅可以減少代碼的復雜程度,還能很大程度地增加代碼的可讀性,減少出錯的可能;
有些同學可能會有疑問,很多項目的數據結構設計的不是那么規范,比如字段名稱可能是一個很奇怪的名字,比如 flag01、flag02,如果這樣的字段從數據庫中查詢出來,通過接口返回,那么會不會造成接口的可讀性太差?
通常我們不會把 model 中的內容直接包裝返回,model 很多的是數據庫和 Java 對象的映射,而傳輸數據的話,通常需要 DTO;我們從數據庫中查詢出來數據放到 model 中,在接口中返回數據之前,把 model 轉換成 DTO,而 DTO 中的屬性需要保證其規范性和見名知意。
Step 6. 創建 Service 層
我們的項目現在已經有了 Dao 層,用于訪問數據,有 Controller 層,用戶提供接口訪問,那么 Controller 是否能直接調用 Dao 中的方法呢?最好不要直接調用!
通常我們會創建一個 Service 層,用于存放業務邏輯,這時候完整的調用流程是:
Controller - Service - Dao
創建 Service package 之后,在里面創建一個 UserService :
@Service public class UserService { @Autowired UserDao userDao; public User queryUserById(int id) { return userDao.queryUserById(id); } }
Step 7. 在 Controller 層增加接口
增加一個接口,通過 userId 查詢客戶信息,并返回客戶信息:
@RequestMapping(value = "/queryUser/{id}") @ResponseBody public String queryUserById(@PathVariable("id") int id){ User user = userService.queryUserById(id); return user == null ? "User is not find" : user.toString() ; }
Step 8. 測試驗證
在瀏覽器或客戶端中訪問接口進行調試測試,可以查詢到客戶信息:
http://127.0.0.1:8088/queryUser/1 User [id=1, userId=dashu, userName=大叔, gender=M, age=18, telephone=null]
03MyBatis 的其他操作
只給出關鍵代碼,完整代碼請參考本章節的項目代碼。
01. 新增
@Insert("INSERT INTO USER(userId, userName, gender, age) values" + " (#{userId}, #{userName}, #{gender}, #{age})") public void insertUser(User user);
02. 修改
@Update("UPDATE USER SET mobilephone = #{telephone} WHERE id = #{id}") public void updateUserTel(User user);
03. 刪除
@Delete("DELETE FROM USER WHERE id = #{id}") public void deleteUserById(@Param("id") int id);
4代碼完善
上面我們就完成了 Spring Boot 和 MyBatis 最簡單的集成,可以正常地讀取數據庫做 CRUD 了,但是因為是最簡單的集成,所以有一些細節需要完善一下,比如:
參數都在顯示在了 url 中;
直接返回 Object.toString(), 不是很友好;
查詢不到數據或發生異常,沒有做特殊處理;
下面讓我們逐步完善
01. 使用 Json 作為參數發送 Post 請求
如果嚴格地遵守 Restful 風格,那么需要遵守:
查詢:GET /url/xxx
新增:POST /url
修改:PUT /url/xxx
刪除:DELETE /url/xxx
在這里我們就單純地認為把參數寫在 url 中,容易一眼就看到我們的參數內容,并且如果參數比較多的時候會造成 url 過長,所以通常我們比較習慣使用 Json 作為參數發送 Post 請求。比如新增 User 的接口可以寫成這樣:
新增 DTO package 并新建 UserDTO:
//使用了 Josn 作為參數,需要設置 headers = {"content-type=application/json"} //@RequestBody UserDto userDto 可以讓 JSON 串自動和 UserDto 綁定和轉換 @RequestMapping(value = "/insertUser2",headers = {"content-type=application/json"}) @ResponseBody public String insertUser2(@RequestBody UserDto userDto){ //DTO 轉成 Model User user = new User(); user.setUserId(userDto.getUserId()); user.setUserName(userDto.getUserName()); user.setGender(userDto.getGender()); user.setAge(userDto.getAge()); userService.insertUser(user); return "Success" ; }
新增 User 的接口:
//使用了 Josn 作為參數,需要設置 headers = {"content-type=application/json"} //@RequestBody UserDto userDto 可以讓 JSON 串自動和 UserDto 綁定和轉換 @RequestMapping(value = "/insertUser2",headers = {"content-type=application/json"}) @ResponseBody public String insertUser2(@RequestBody UserDto userDto){ //DTO 轉成 Model User user = new User(); user.setUserId(userDto.getUserId()); user.setUserName(userDto.getUserName()); user.setGender(userDto.getGender()); user.setAge(userDto.getAge()); userService.insertUser(user); return "Success" ; }
讓我們調用接口測試一下:
{ "userId": "lisi", "userName": "李四", "gender": "F", "age": "40", "telephone": "18600000000" }
02. 規范回參
直接返回 Object.toString(), 不是很友好;
讓我們設計一個簡單的回參對象,包括 code-狀態碼,message-異常信息描述,data-數據:
public class JsonResponse { private String code; private String message; private Object data; //省略 set、get 方法 }
其中 code 我們就參考 Http 狀態碼,使用常用的幾個:
public class ResponseCode { public static final String SUCCESS = "200";//查詢成功 public static final String SUCCESS_NULL = "204";//查詢成功,但是沒有數據 public static final String PARAMETERERROR = "400";//參數錯誤 public static final String FAIL = "500";//服務器異常 }
這時我們再來重寫一下查詢接口:
@RequestMapping(value = "/queryUser2") @ResponseBody public JsonResponse queryUser2ById(@RequestBody UserDto userDto){ JsonResponse res = new JsonResponse(); //省略參數校驗 User user = userService.queryUserById(userDto.getUserId()); if(user != null){ //能查詢到結果,封裝到回參中 res.setCode(ResponseCode.SUCCESS); res.setData(user);; }else{ //如果查詢不到結果,則返回 '204' res.setCode(ResponseCode.SUCCESS_NULL); res.setMessage("查詢不到數據"); } return res; }
調用結果可以看到封裝后的回參,看起來是不是規范了很多:
{ "code": "200", "message": null, "data": { "id": 3, "userId": "lisi", "userName": "李四", "gender": "F", "age": 40, "telephone": null } }
03. 異常處理
如果代碼在運行過程中發生異常,那么改如何處理呢?直接把異常信息返回給前端么?這樣做對調用方不是很友好,通常我們把錯誤日志打印到本地,給調用方返回一個異常狀態碼即可。
Service、Dao 層的集成都往上拋:
public User queryUserById(int userId) throws Exception{ return userDao.queryUserById(userId); }
在 Controller 層抓住異常,并封裝回參:
User user = new User(); try { user = userService.queryUserById(userDto.getId()); } catch (Exception e) { res.setCode(ResponseCode.FAIL); res.setMessage("服務異常"); }
4MyBatis 常見問題
01. 為什么 MyBatis 被稱為半自動 ORM 框架?
有半自動就會有全自動;
Hibernate 就屬于全自動 ORM 框架,使用 Hibernate 可以完全根據對象關系模型進行操作,也就是指操作 Java 對象不需要寫 SQL,因此是全自動的;而 MyBatis 在關聯對象的時候,需要手動編寫 SQL 語句,因此被稱作“半自動”。
02. 使用注解還是 XML?
相信大部分項目使用 MyBatis 的時候,都是使用 XML 配置 SQL 語句,而我們課程中的例子,都是使用注解的方式,那么這兩者有什么區別呢?我們在實際開發中,要如何選擇呢?
首先官方是比較推薦使用 XML 的,因為使用注解的方式,拼接動態 SQL 比較費勁兒,如果你們的 SQL 比較復雜,需要多表關聯,還是使用 XML 比較好;而且現在也有很多插件,可以自動生成 MyBatis XML。
但是事物總是有兩方面的,復雜的 SQL 并不是值得驕傲的事情,如果你們的項目能做到沒有復雜 SQL 的話,使用注解會是更好的選擇(我們現在的項目 95% 以上的 SQL 都是單表查詢)。
03. #{} 和 ${} 的區別是什么?
${} 是字符串替換,#{} 是預編譯處理;使用 #{} 可以防止 SQL 注入,提高系統安全性。
04. 如何做批量插入?
注解的方式同樣可以使用動態 SQL :
@Insert({ "<script>" + "INSERT INTO USER(userId, userName, gender, age) values" + "<foreach collection='userList' item='item' index='index' separator=','>" + " (#{item.userId}, #{item.userName}, #{item.gender}, #{item.age})" + "</foreach>" + "</script>" }) public void insertUserList(@Param(value="userList") List<User> userList);
到此,關于“總結從單體架構到分布式數據持久化,ORM框架之Mybatis”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。