## 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三种基本状态),请注意避免导致误持久化,详见[开发经验章节文档](https://www.kancloud.cn/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%
![](https://img.kancloud.cn/39/c7/39c72beb35767cc496ed6b380ecb647c_1069x655.png)
- 通过 [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工具包
- 线程池
- 其他小经验技巧
- 更新日志及步骤
- 常见问题