tkmybatis-功能扩展

tkmybatis是国人团队开发的一款MyBatis扩展框架,它基于MyBatis实现了很多高级ORM功能,使得我们使用MyBatis框架时,既能获得部分类似Hibernate这种全功能ORM的开发体验,也不失MyBatis的灵活性。

项目主页:https://mybatis.io/

类似的框架还有MyBatis-Plus,这俩功能类似,但细节上有一些区别,我们根据实际情况选择即可。

引入Maven依赖和工程配置

tkmybatis提供了SpringBoot的起步依赖,我们直接引入即可。

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.3.1</version>
</dependency>
<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper-spring-boot-starter</artifactId>
    <version>4.2.1</version>
</dependency>
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.3</version>
</dependency>

其中的pagehelper是同一作者的另外一个分页插件项目,和tkmybatis一般结合使用实现分页功能。

通用CRUD接口

tkmybatis最强大的功能就是通用CRUD接口,我们的Mapper接口继承一个BaseMapper后,可以不编写XML映射文件就能实现基本的CRUD功能。BaseMapper有许多通用的方法,我们可以直接调用这些方法来进行基本的CRUD操作,下面我们直接看一个例子。

User.java

package com.gacfox.mybatisdemo.model;

import lombok.Data;

import javax.persistence.*;

@Data
@Table(name = "t_user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    private Long userId;
    @Column(name = "username")
    private String username;
    @Column(name = "password")
    private String password;
}

实体类代码中,我们用JPA注解标注了相关的类和字段,这部分内容可以参考JPA相关章节,tkmybatis也会读取这些注解并自动进行字段和表的映射。

注意:如果列名为关键字,如keydesc等,@Column注解的name属性中也要使用反引号包裹,否则SQL执行时会报错!

UserMapper.java

package com.gacfox.mybatisdemo.mapper;

import com.gacfox.mybatisdemo.model.User;
import org.apache.ibatis.annotations.Mapper;
import tk.mybatis.mapper.common.BaseMapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

Mapper代码非常简洁,我们直接继承了一个BaseMapper,其中用泛型指定了实体类,此外我们没有任何的实现代码。

UserServiceImpl.java

package com.gacfox.mybatisdemo.service.impl;

import com.gacfox.mybatisdemo.mapper.UserMapper;
import com.gacfox.mybatisdemo.model.User;
import com.gacfox.mybatisdemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service("userService")
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public User getUserById(Long userId) {
        return userMapper.selectByPrimaryKey(userId);
    }
}

使用Mapper时,我们直接调用了通用Mapper中的selectByPrimaryKey方法,它是一个根据主键获取实体类对象的通用方法。除了selectByPrimaryKey,还有许多其他好用的通用方法,这里就不列举了。

Criteria查询

除了通用Mapper,tkmybatis也支持构建Criteria查询。

package com.gacfox.mybatisdemo.mapper;

import com.gacfox.mybatisdemo.model.User;
import org.apache.ibatis.annotations.Mapper;
import tk.mybatis.mapper.common.BaseMapper;
import tk.mybatis.mapper.common.ExampleMapper;

@Mapper
public interface UserMapper extends BaseMapper<User>, ExampleMapper<User> {
}

这里我们的Mapper继承了ExampleMapper,它能够使得我们的Mapper接收tkmybatis提供的Example对象。

package com.gacfox.mybatisdemo.service.impl;

import com.gacfox.mybatisdemo.mapper.UserMapper;
import com.gacfox.mybatisdemo.model.User;
import com.gacfox.mybatisdemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

@Service("userService")
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public User getUserById(Long userId) {
        Example example = new Example(User.class);
        example.createCriteria().andEqualTo("userId", userId);
        return userMapper.selectOneByExample(example);
    }
}

代码比较简单,我们直接构造了一个条件查询的Criteria,使用Mapper查询即可。这里除了andEqualTo还有很多方法可用,这里就不展开介绍了。

此外有一点尤其要注意,Criteria有一个方法criteria.andCondition(),我们在使用这个方法的使用千万不要用字符串拼接SQL,错误示例:criteria.andCondition("id='" + id + "'"),这种写法存在严重的SQL注入漏洞,如果id字段为用户传入,黑客是可以通过SQL注入方式直接下载整个数据库的!正确示例:criteria.andCondition("id=", id)

PageHelper分页插件

PageHelper是一个能够高效实现分页查询的插件,它的使用非常简单,下面是一个例子。

package com.gacfox.mybatisdemo.service.impl;

import com.gacfox.mybatisdemo.mapper.UserMapper;
import com.gacfox.mybatisdemo.model.User;
import com.gacfox.mybatisdemo.service.UserService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service("userService")
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public PageInfo<User> getUserByPage(Integer pageNum, Integer pageSize) {
        List<User> users;
        try (Page<Object> ignored = PageHelper.startPage(pageNum, pageSize)) {
            users = userMapper.selectAll();
        } finally {
            PageHelper.clearPage();
        }
        return new PageInfo<>(users);
    }
}

PageHelper.startPage()会在当前线程初始化一个ThreadLocal参数存储分页信息,下一条执行SQL分页查询时,PageHelper会自动对其拦截并注入分页信息,因此我们下一条userMapper.selectAll()操作就会自动进行分页查询了。PageHelper.clearPage()用于清除当前线程中的分页参数,如果不清除这些参数,后续的查询可能会意外地应用相同的分页参数,导致查询结果不符合预期。因此,在分页查询完成后,调用PageHelper.clearPage()是一个良好的实践,确保线程安全。

PageHelper插件还提供了一个PageInfo封装类,它封装了和分页相关的信息,包括当前页码、下一页页码、页码列表和分压大小,分页查询时我们通常都需要这些参数展示分页按钮,因此这些信息从PageInfo中取即可。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。