Skip to content
Exrick edited this page May 5, 2020 · 8 revisions

JPA与Mybatis-Plus随意切换(不用配置,可同时使用)

项目持久层默认推荐使用JPA,更易快速上手,OOP首先应满足面向对象的要求,性能优化时再考虑面向数据库。即项目工期短、快速交付推荐JPA,复杂业务逻辑需联表查询或性能优化时可选择Mybatis-Plus自定义SQL

由于目前单表设计+物理删除,批量删除默认使用循环单条删除设计,目的为了方便以后手动删除关联数据。若使用逻辑删除可考虑使用批量删除

  • 不想写SQL?Spring Data JPA 了解一下

  • JPA开发提醒(具体请了解JPA持久化上下文 参考Hibernate三种基本状态),请注意避免导致误持久化,详见开发经验章节文档

    @PersistenceContext
    private EntityManager entityManager;
    
    User u = userService.findByUsername("username");
    // 清除持久上下文环境 避免后面语句导致持久化 或将其设置为游离态entityManager.detach(u);
    entityManager.clear();
    u.setPassword(null); // 持久化状态时此处会自动执行update语句更新至数据库中
  • 复杂业务逻辑JPA联表太蛋疼?MyBatis-Plus 了解一下

  • JPA与MybatisPlus同时使用时需注意实体类注解区别,更多请见官方文档,常用注解区别:

    // 表名
    JPA: @Table(name = "t_user")
    MP:  @TableName("t_user")
    // 排除非表字段
    JPA: @Transient
    MP:  @TableField(exist=false)

JPA增删改查示例

  • DAO层
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官网文档

  • Service层
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层:
@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层:
@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);
    }
}

Mybaiti-Plus增删改查示例

已更新至3.x版本

  • DAO层
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);
}

到底选择JPA还是Mybatis?

国内外现状

  • 通过 Google Trends,可以发现过去12个月(From 2020.05)对于MyBatis的关注主要集中在中日韩

国内流行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),两者不会冲突