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也会读取这些注解并自动进行字段和表的映射。
注意:如果列名为关键字,如key、desc等,@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中取即可。