## 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),两者不会冲突