目录
前言
mapper文件,作为Mybatis框架中定义SQL语句和映射关系的配置文件,在引入mybtis-plus后,有了更多的实现方式,本文对此做一个对比总结。
一、传统 Mapper 接口方式
先重温一下原始Mybatis的实现方式,以下所有方式均以用户类 User 作为实体类
- 定义Mapper接口 :为每个实体类创建对应的 Mapper 接口,接口中声明所需的数据库操作方法。例如,针对用户实体类 User,创建 UserMapper 接口。
@Mapper
public interface UserMapper {
// 自定义方法
List<User> selectAllUsers();
}
- 实现Mapper接口 :通常使用 MyBatis 的注解或 XML 配置文件来实现接口中的方法。对于简单方法,可以使用注解;对于复杂查询,使用 XML 配置。
@Select("SELECT * FROM user")
List<User> selectAllUsers();
或者在 UserMapper.xml 文件中配置:
<select id="selectAllUsers" resultType="com.example.entity.User">
SELECT * FROM user
</select>
- 优点 :开发人员对数据库操作有较高的控制度,能够灵活地定义各种复杂的 SQL 查询;适合处理复杂的业务场景,尤其是涉及到多表关联等复杂操作时。
- 缺点 :需要为每个实体类编写对应的 Mapper 接口和实现代码,当实体类较多时,工作量较大;代码重复性较高,对于简单的 CRUD 操作,需要重复编写类似的接口方法。
- 适用场景 :适用于业务逻辑复杂、需要精确控制数据库操作的项目,尤其是涉及到大量自定义 SQL 查询的场景。
二、继承 BaseMapper 的方式
这是 MyBatis-Plus 提供的一种简化方式,利用其内置的通用 Mapper 接口来快速实现常见的数据库操作。
- 定义Mapper接口 :让自定义的 Mapper 接口继承 MyBatis-Plus 提供的
BaseMapper
接口。这样就可以直接使用BaseMapper
中定义的通用的增删改查方法。
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 可以添加自定义的其他方法
}
- 使用通用方法 :通过继承
BaseMapper
,可以直接使用如insert()
(插入)、deleteById()
(根据 ID 删除)、updateById()
(根据 ID 更新)、selectById()
(根据 ID 查询)、selectList()
(查询列表)等方法,无需自己实现。
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public void addUser(User user) {
userMapper.insert(user);
}
public User getUserById(Long id) {
return userMapper.selectById(id);
}
}
- 优点 :极大地简化了代码编写,减少了重复代码;对于简单的 CRUD 操作,开发效率显著提高。
- 缺点 :对于一些复杂的查询和操作,可能需要结合自定义的 SQL 来实现,此时需要在继承
BaseMapper
的基础上扩展自定义方法。 - 适用场景 :适用于对实体类进行简单的增删改查操作的场景,能够快速开发出基本的数据访问功能。
三、自定义通用 Mapper 的方式
如果对 MyBatis-Plus 的默认 BaseMapper
提供的方法不满意,或者需要在多个项目中复用自定义的通用 Mapper,可以自定义通用 Mapper。
- 创建自定义通用Mapper接口 :定义一个接口,例如
MyBaseMapper
,并根据需求定义通用的方法。
public interface MyBaseMapper<T> {
int insertBatchSomeColumn(List<T> entityList);
List<T> selectByCustomCondition(Map<String, Object> params);
}
- 实现自定义通用Mapper :可以通过继承 MyBatis-Plus 的
BaseMapper
并实现自定义接口,或者完全自己实现通用方法。
public class MyBaseMapperImpl<T> extends BaseMapper<T> implements MyBaseMapper<T> {
@Override
public int insertBatchSomeColumn(List<T> entityList) {
// 自定义实现逻辑
}
@Override
public List<T> selectByCustomCondition(Map<String, Object> params) {
// 自定义实现逻辑,可以使用 MyBatis 的注解或 XML 配置
}
}
- 使用自定义通用Mapper :让具体的 Mapper 接口继承自定义的通用 Mapper 接口。
@Mapper
public interface UserMapper extends MyBaseMapper<User> {
}
- 优点 :可以根据项目需求灵活定制通用的数据库操作方法,提高代码的可复用性和可维护性。
- 缺点 :需要花费一定的时间进行自定义通用 Mapper 的设计和实现,增加了开发的初始工作量;如果设计不合理,可能会影响后续的使用和扩展。
- 适用场景 :适用于有特殊数据库操作需求,或者需要在多个项目中共享通用 Mapper 的情况。
四、使用 MyBatis-Plus 的 ActiveRecord 模式
这种方式让实体类本身承担数据访问的职责,无需编写 Mapper 接口。
- 实体类继承 Model :实体类继承 MyBatis-Plus 的
Model
类,并实现相关的主键指定方法。
public class User extends Model<User> {
private Long id;
private String name;
private Integer age;
}
- 使用实体类进行数据操作 :通过实体类的实例直接调用数据访问方法,如
insert()
、delete()
、update()
、selectById()
等。
@Service
public class UserService {
public void addUser(User user) {
user.insert();
}
public User getUserById(Long id) {
return User.selectById(id);
}
}
- 优点 :减少了 Mapper 接口的编写工作,使代码更加简洁;对于简单的 CRUD 操作,开发效率较高。
- 缺点 :实体类承担了过多的数据访问职责,导致类的职责不清晰;在处理复杂的业务逻辑和多表关联时,代码的可维护性和可读性较差。
- 适用场景 :适用于小型项目或快速原型、本地应用开发,主要用于简单的数据操作场景。
五、使用 MyBatis-Plus 的 IService 接口
这种方式结合了 MyBatis-Plus 的 Mapper 和 Service 层的通用接口,提供更丰富的预设方法。
- 定义Mapper接口继承 BaseMapper :和前面提到的继承
BaseMapper
的方式一样,为实体类创建对应的 Mapper 接口。
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
- 定义 Service 接口继承 IService :让自定义的 Service 接口继承 MyBatis-Plus 提供的
IService
接口,IService
提供了一些常用的业务逻辑方法。
public interface UserService extends IService<User> {
// 自定义业务逻辑方法
}
- 实现 Service 接口 :在 Service 实现类中,注入对应的 Mapper 接口,并使用
IService
提供的方法以及自定义的业务逻辑。
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public void customMethod() {
// 使用 IService 提供的方法或自定义逻辑
}
}
- 优点 : IService中提供的丰富的通用方法,提高开发效率。
- 缺点 :造成设计层面sql对service的入侵。
- 适用场景 :适用于小型项目或快速开发项目。
六、使用建议
- 小型项目或快速开发项目
例如个人毕设,单接口服务,简单的管理系统,这种类似一次性开发的项目,当然是怎么快怎么方便怎么来,这时候就十分推荐使用MyBatis-Plus的Model+BaseMapper+IService
,可以给开发节省很多时间。 - 本地应用或脚本
例如在本地做数据处理,开发数据引擎,这时候实际上是没有mvc那一说法的,因为大量的操作就是操作数据,使用ActiveRecord
模式不会有太大的负面印象,复杂的操作使用QueryWrapper
或LambdaQueryWrapper
,Model类里面也有对应的方法。 - 中型项目但是人少
国内大部分是中小企业,开发人员就五六个,相比于耦合带来的问题,开发进度更重要一些,使用BaseMapper+IService
就好,AR模式对业务的侵入还是大了些。 - 大型项目最优实践
创建自己的MyBaseMapper
,继承MyBatis-Plus的BaseMapper
,在这里编写公共方法,例如IService
提供的一些批量处理方法,所有的mapper接口再继承MyBaseMapper
关于sql怎么写:
单表的简单操作直接用BaseMapper
提供的方法
单表的复杂操作使用QueryWrapper
或LambdaQueryWrapper
进行组装
多表的简单联查使用mybatis的注解在mapper接口中
多表的复杂联查在mapper.xml中编写