## JPA與Mybatis-Plus隨意切換(不用配置,可同時使用)
> 項目持久層默認推薦使用JPA,更易快速上手,OOP首先應滿足面向對象的要求,性能優化時再考慮面向數據庫。即項目工期短、快速交付推薦JPA,復雜業務邏輯需聯表查詢或性能優化時可選擇Mybatis-Plus自定義SQL
> 由于目前單表設計+物理刪除,批量刪除默認使用循環單條刪除設計,目的為了方便以后手動刪除關聯數據。若使用邏輯刪除可考慮使用批量刪除
- **不想寫SQL?[Spring Data JPA](https://docs.spring.io/spring-data/jpa/docs/2.0.6.RELEASE/reference/html/#jpa.query-methods.query-creation) 了解一下**
- JPA開發提醒(具體請了解JPA持久化上下文 參考Hibernate三種基本狀態),請注意避免導致誤持久化,詳見[開發經驗章節文檔](http://www.hmoore.net/exrick/xboot/1684589)
```java
@PersistenceContext
private EntityManager entityManager;
User u = userService.findByUsername("username");
// 清除持久上下文環境 避免后面語句導致持久化 或將其設置為游離態entityManager.detach(u);
entityManager.clear();
u.setPassword(null); // 持久化狀態時此處會自動執行update語句更新至數據庫中
```
- **復雜業務邏輯JPA聯表太蛋疼?[MyBatis-Plus](http://mp.baomidou.com) 了解一下**
- JPA與MybatisPlus同時使用時需注意實體類注解區別,更多請見官方文檔,常用注解區別:
```java
// 表名
JPA: @Table(name = "t_user")
MP: @TableName("t_user")
// 排除非表字段
JPA: @Transient
MP: @TableField(exist=false)
```
### JPA增刪改查示例
- DAO層
~~~java
public interface UserDao extends XbootBaseDao<User,String> {
/**
* 通過用戶名和狀態獲取用戶
* @param username
* @param status
* @return
*/
List<User> findByUsernameAndStatus(String username, Integer status);
}
~~~
= "select * from t_user where username = username and status = status"
更多JPA示例詳見[Spring Data JPA官網文檔](https://docs.spring.io/spring-data/jpa/docs/2.0.6.RELEASE/reference/html/#jpa.query-methods.query-creation)
- Service層
~~~java
public interface UserService extends XbootBaseService<User,String> {
/**
* 通過用戶名獲取用戶
* @param username
* @return
*/
User findByUsername(String username);
/**
* 多條件分頁獲取用戶
* @param user
* @return
*/
Page<User> findByCondition(User user, Pageable pageable);
}
~~~
- ServiceImpl層:
~~~java
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public UserDao getRepository() {
return userDao;
}
@Override
public User findByUsername(String username) {
List<User> list = userDao.findByUsernameAndStatus(username, CommonConstant.USER_STATUS_NORMAL);
if(list!=null&&list.size()>0){
return list.get(0);
}
return null;
}
@Override
public Page<User> findByCondition(User user, Pageable pageable) {
return userDao.findAll(new Specification<User>() {
@Nullable
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
Path<String> usernameField = root.get("username");
Path<String> mobileField = root.get("mobile");
Path<String> emailField = root.get("email");
Path<Integer> sexField=root.get("sex");
Path<String> departmentIdField = root.get("departmentId");
Path<Date> createTimeField = root.get("createTime");
List<Predicate> list = new ArrayList<Predicate>();
//模糊搜素
if(StrUtil.isNotBlank(user.getUsername())){
list.add(cb.like(usernameField,'%'+user.getUsername()+'%'));
}
//性別
if(user.getSex()!=null){
list.add(cb.equal(sexField,user.getSex()));
}
//補充示例 between查詢
list.add(cb.between(createTimeField, 起始時間, 結束時間);
//補充示例 or查詢
if(StrUtil.isNotBlank(搜索詞)){
Predicate p1 = cb.like(mobileField,'%'+搜索詞+'%');
Predicate p2 = cb.like(emailField,'%'+搜索詞+'%');
list.add(cb.or(p1, p2));
}
//補充示例 in查詢
List<String> ids = new ArrayList();
list.add(departmentIdField.in(ids));
Predicate[] arr = new Predicate[list.size()];
cq.where(list.toArray(arr));
return null;
}
}, pageable);
}
}
~~~
- Controller層:
~~~java
@RestController
@RequestMapping("/user")
public class UserController extends XbootBaseController<User, String> {
@Autowired
private UserService userService;
@Override
public UserService getService() {
return userService;
}
@RequestMapping(value = "/getByCondition", method = RequestMethod.GET)
@ApiOperation(value = "多條件分頁獲取用戶列表")
public Result<Page<User>> getByCondition(@ModelAttribute User user,
@ModelAttribute SearchVo searchVo,
@ModelAttribute PageVo pageVo){
Page<User> page = userService.findByCondition(user, searchVo, PageUtil.initPage(pageVo));
return new ResultUtil<Page<User>>().setData(page);
}
}
~~~
- [官方Github示例](https://github.com/spring-projects/spring-data-examples/tree/master/jpa)
### Mybaiti-Plus增刪改查示例
> 已更新至3.x版本
- DAO層
~~~java
public interface UserMapper extends BaseMapper<User> {
}
~~~
- Service層
~~~
public interface IUserService extends IService<User> {
}
~~~
- ServiceImpl層:
~~~
@Service
public class IUserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Autowired
private UserMapper userMapper;
}
~~~
- Controller層:
~~~
@RestController
@RequestMapping("/xboot/user")
public class UserController {
@Autowired
private IUserService iUserService;
@RequestMapping(value = "/getByPage", method = RequestMethod.GET)
@ApiOperation(value = "分頁獲取")
public Result<Page<User>> getByPage(@ModelAttribute PageVo page){
Page<User> data = iStudentService.page(PageUtil.initMpPage(page));
return new ResultUtil<Page<User>>().setData(data);
}
@RequestMapping(value = "/getByCondition", method = RequestMethod.GET)
@ApiOperation(value = "多條件分頁獲取")
public Result<IPage<User>> save(@ModelAttribute User user,
@ModelAttribute PageVo page){
QueryWrapper<User> qw = new QueryWrapper<User>();
qw.eq("name", "'王'").ne("name", "王")
.gt("age", 18).ge("age", 18)
.lt("age", 18).le("age", 18)
.between("age", 18, 30).notBetween("age", 18, 30)
.like("name", "王").notLike("name", "王")
.isNotNull("name")
.in("age",1,2,3).notIn("age",1,2,3)
.inSql("id", "select id from table where id < 3")
.groupBy("id", "name")
.orderByAsc("id", "name").orderByDesc("id", "name")
.orderBy(true, true, "id", "name")
.having("sum(age) > {0}", 11)
.or().eq("name","老王");
IPage<User> data = iUserService.page(PageUtil.initMpPage(page), qw);
return new ResultUtil<IPage<User>>().setData(data);
}
// 補充常用增刪改查方法示例 其他自行查看接口方法以及官方文檔
// 單條
User user = iUserService.getById(id);
// 所有
List<User> list = iUserService.list();
// 編輯或保存
iUserService.saveOrUpdate(student))
// 通過id刪除
iUserService.removeById(id);
}
~~~
- [官方接口文檔](https://mp.baomidou.com/guide/crud-interface.html#mapper-crud-%E6%8E%A5%E5%8F%A3)
- [條件構造官方文檔](https://mp.baomidou.com/guide/wrapper.html)
## 到底選擇JPA還是Mybatis?
### 國內外現狀
- 從 [2018年全球JVM生態報告](https://snyk.io/blog/jvm-ecosystem-report-2018-platform-application/) 中可以看到Mybatis的使用占比只有6%

- 通過 [Google Trends](https://trends.google.com/trends/explore?q=mybatis),可以發現過去12個月(From 2020.05)對于MyBatis的關注主要集中在中日韓(中方外包)
<img src=https://ooo.0o0.ooo/2020/05/04/FpXqDUWGavP5SJA.png width=700/>
### 國內流行Mybatis原因
- 大廠帶節奏。阿里一開始用 iBatis,大量的老系統基于 iBatis/MyBatis 的,培訓機構跟風
- 簡單學習成本低。對于復雜性需求的靈活性高,國內絕大部分項目都是面向表結構編程的,分布式、流量和性能決定了需要經常進行優化
- 面向領導編程。國內項目需要大量報表統計,需要提供給領導作為決策
- Hibernate 學習成本稍高。雖然Spring Data JPA 非常簡單的,但是后期調試跟蹤問題較麻煩
### 國外流行JPA原因
- 很多老外對 Mybatis 的認知還停留在 iBatis 階段,但 Mybatis 發展到今天,例如MyBatis-PLus已經非常完美地做好了自動封裝數據對象,支持的插件也比較豐富。對于常見的增刪改查,也不需要自己寫一行代碼,這已經無限接近于 Hibernate 的能力了
- 喜歡 OOP、DDD(Domain-Driven Design,領域對象驅動設計,04年提出的理論),認為寫 SQL 不優雅,用 JPA 的核心是關注對象建模,而不是關心底層數據庫映射
- 有些老外在技術選型時,不會考慮除 Spring 這種知名框架外的其他技術,使用技術也是有慣性的
- 老外的項目在數據體量和種類上完全達不到國內的水平。所以,他們對于性能上的渴求度沒有那么高。追求的是穩定,可維護性好,需求主要是在業務上,技術層面較少考慮
### 小結
- **技術是服務于業務的,沒有絕對完美的技術,選擇取決于項目需求**
- 面向對象更利于移植,數據對象不依賴于數據源;面向SQL更利于優化,因為SQL可以優化的點太多了
- 對于國內生態來說,**企業級應用,可以考慮JPA;互聯網應用,當然優先Mybatis**
> 現在XBoot一站式快速開放平臺為你提供了更靈活的選擇,全都要!**單表JPA,多表MP**(項目工期短、快速交付JPA;復雜業務邏輯需聯表查詢或性能優化時Mybatis-Plus),兩者不會沖突
- 前言&版本說明
- 概念
- XBoot 是什么?
- 系統架構
- 主要使用的開源組件
- 角色控制訪問權限(RBAC)
- 用戶手冊
- 系統配置
- 工作流使用配置
- 定時任務調度
- 智能助手客服機器人
- 項目本地運行
- 后端運行
- 前端運行
- 項目結構說明
- 附:使用Oracle等數據庫
- 模塊化版本
- 后端開發指南
- 基本開發指南
- 前后端數據交互標準
- 工具類及數據權限
- 代碼生成器-30秒搞定CRUD
- 增刪改查CRUD
- 日志類型注解擴展
- 邏輯刪除
- 各驗證碼使用及配置
- 接口文檔使用及認證
- 前端開發指南
- 基本開發指南
- 主題/Logo/首頁等配置
- 路由菜單配置
- 多語言國際化配置
- 自定義圖標icon
- 工具類及數據獲取
- 其他說明
- 完整版開發指南
- 前端Vue代碼生成器
- Activiti工作流
- 單點登錄配置
- 智能助手/客服機器人
- MinIO對象存儲服務搭建
- 第三方社交賬號配置
- 短信開發/站內消息/郵件
- Vaptcha驗證碼
- 禁用詞使用
- 前端移除CDN
- 其他說明
- 開放平臺及單點登錄
- 開放平臺使用指南
- Web接入開發流程
- 單點登錄開發指南
- 微信小程序端開發指南
- 項目導入與開發必讀
- 業務組件
- 產品組件(小)
- 產品組件(大)
- 優惠券組件
- 評論列表組件
- 紅包組件
- 推薦商品組件
- 頁面設計
- 商品詳情頁及SKU設計
- 通用方法工具類說明
- 開發經驗與踩坑分享
- Uniapp端開發指南
- APP后端開發指南
- Uniapp前端開發指南
- 開發新功能示例
- 后端開發新模塊
- 前端開發新頁面
- 測試
- SonarQube代碼質量管理
- TestNG單元測試
- ExtentReports測試報告
- Selenuim自動化Web測試
- Appuim自動化App測試
- JMeter壓測性能測試
- 部署
- Spring Boot配置
- 快速部署
- 后端部署
- 前端部署
- 前端部署優化
- Docker容器化部署
- 服務器配置
- 持續集成
- GitLab
- GitLab CI
- XBoot 腳本參考
- Jenkins
- Jenkins安裝
- XBoot CI參考
- DevOps環境搭建
- 組件安裝列表
- 開發設計規范
- 分支管理
- 數據庫設計規范
- Redis使用規范
- Java基礎開發規范
- Rest API規范
- 項目結構規范
- 前端開發規范
- 前端設計規范
- 項目搭建分享
- 后端相關
- SpringBoot 2.x區別總結
- Spring Security整合JWT
- Spring Security動態權限管理
- Spring Boot 2.x整合Quartz
- Spring Boot 2.x整合Websocket
- Spring Boot 2.x整合Activiti工作流以及模型設計器
- Spring Boot + Security全局跨域配置
- 前端相關
- axios請求封裝 統一異常處理
- 動態路由菜單加載
- 多維度控制權限至按鈕顯示
- 發送消息圖標紅點實時顯示
- 動態組件單頁操作
- XBoot助你【告別996】
- 業務開發踩坑
- 你會用開發神器IDEA嗎
- Lombok你知道多少
- 你還在手動校驗參數嗎
- 你真的會用JPA嗎
- Lamda表達式
- Stream流式API
- 告別資源關閉
- Optional避免null
- 谷歌Guava工具包
- 線程池
- 其他小經驗技巧
- 更新日志及步驟
- 常見問題