本文适合初次学习通用Mapper新鸟阅览,老生常谈,从三个大方向入手----是什么?能干什么?怎么用?
目录
三、怎么使用通用Mapper?这里基于SpringBoot实现
一、通用Mapper是什么?
简单来说,它就是个辅助mybatis极简单表开发的组件,只能算插件,还算不上框架。它不是为了替代mybatis,而是让mybatis的开发更方便----可以自动生成sql语句,方便开发人员进行ORM,提供极其方便的单表(不支持通用的多表联合查询)增删改查。
二、通用Mapper能干什么?
说到这里就不得不提一提为什么会产生通用Mapper----使用原生Mybatis遇到的问题:
1.mapper.xml文件里有大量的sql,当数据库表字段变动,配置文件就要修改
2.分页麻烦,除了传参page、pageSize,还需要返回条目总数count
3.数据库可移植性差:如果项目更换数据库,比如oracle-->mysql,mapper.xml中的sql要重新写
4.批量操作,批量插入,批量更新,需要自写动态SQL使用,麻烦
也正是因为要解决这些问题,通用Mapper就出现了----主要就用于解决单表的增删改查,不需要编写SQL,不需要在DAO中增加方法,只要写好实体类,就能支持相应的增删改查方法,逻辑更清晰,结构整洁,可以按照模板化编程。也一一对应的解决了原生mybatis所存在的以上问题,具体怎么解决见下文。
缺点: 不灵活,有些SQL难以实现
需要在Java代码中加入大量逻辑判断,确定模板方法的准确性
需要大量条件判断,如非空,或者需要手动置空,才能避免覆盖
三、怎么使用通用Mapper?这里基于SpringBoot实现
整合步骤:
1.创建项目,添加依赖
2.创建公用Mapper接口继承通用Mapper和MySqlMapper
3.在配置文件application.yml中配置数据库,MyBatis等
4.在启动类上别忘了加扫描Mapper包
5.使用通用Mapper
1.创建项目,添加相关依赖
<!--Mybatis(必须)-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<!--分页插件 (需要分页时使用) -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>1.2.5</version>
</dependency>
<!--通用Mapper插件依赖(必须)-->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.0.2</version>
</dependency>
<!--lombok,简化实体类代码,需要时写-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--数据库连接依赖(必须)-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
<scope>runtime</scope>
</dependency>
<!--web依赖(必须)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.创建公用创建公用Mapper接口继承通用Mapper和MySqlMapper
package com.cloudsw.base;
import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.common.MySqlMapper;
/**
* @Description: 公用Mapper接口
* @Date: 2021/3/29
* @Author lijintao
*/
@component
public interface IBaseMapper<T> extends Mapper<T>,MySqlMapper<T> {
}
注意:mappers 是指向自定以的基础通用mapper接口,该接口不能和普通接口放在一起(不然扫描自定义mapper时会报错),推荐放在工具包里,这里要注意Mapper<T>,MySqlMapper<T>都是tk包下的,Mapper<T>提供了基础的增删改查功能,MySqlMapper<T>提供了批量新增的功能,然后使用我们去继承这个mapper就有CRUD的功能
3.在配置文件application.yml中配置数据库,MyBatis等
#端口配置
server:
port: 8088
#jdbc配置
spring:
datasource:
name: clodsw-portal-dev
url: jdbc:mysql://localhost:3306/cloudsw_portal?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC&autoReconnect=true&allowPublicKeyRetrieval=true
username: 123456
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
#mybatis配置
mybatis:
#定义xml文件位置,不是必须的,如果需要在xml写sql请配置此选项
mapper-locations: classpath:mapper/*.xml
#实体类所在包名
type-aliases-package: com.cloudsw.dto
#通用mapper配置
mapper:
#公用接口类路径
mappers: com.cloudsw.base.IBaseMapper
identity: MYSQL
4.在启动类上别忘了加扫描Mapper包
package com.cloudsw;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication
@MapperScan("com.cloudsw.mapper") // 需要将继承的Mapper接口添加到Mybatis配置中
public class MapperApplication {
public static void main(String[] args) {
SpringApplication.run(MapperApplication.class, args);
}
}
注:此处MapperScan为tk.mybatis.spring.annotation.MapperScan,非org.mybatis.spring.annotation.MapperScan;
5.使用通用Mapper
5.1 定义实体类
package com.cloudsw.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Repository;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
@Data //使用lombok注解,可以免写getter,setter方法
@Repository //为什么 @Repository 只能标注在 DAO 类上呢?这是因为该注解的作用不只是将类识别为Bean,同时它还能将所标注的类中抛出的数据访问异常封装为 Spring 的数据访问异常类型
@Table(name = "msg_todo")//mybatis通用接口mapper依赖JPA实体类采用JPA
public class MsgTodo implements Serializable {
private static final long serialVersionUID=1L;
@Id //代表主键,如果一个类中有多个这个注解代表 联合主键
@GeneratedValue(strategy = GenerationType.IDENTITY) //主键策略
private Long id;
@Column(name="name")
private String name;
@Column(name="receive_time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date receiveTime;
@Column(name="create_code")
private String createCode;
@Column(name="create_name")
private String createName;
@Column(name="create_time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@Column(name="content")
private String content;
@Column(name="send_type")
private Integer sendType;
@Transient //代表不是数据表中的字段,只是普通属性
private String haha;
}
注:
实体类按照如下规则和数据库表进行转换,注解全部是JPA中的注解:
- 表名默认使用类名,驼峰转下划线(只对大写字母进行处理),如
UserInfo
默认对应的表名为user_info
。- 表名可以使用
@Table(name = "tableName")
进行指定,对不符合第一条默认规则的可以通过这种方式指定表名.- 字段默认和
@Column
一样,都会作为表字段,表字段默认为Java对象的Field
名字驼峰转下划线形式.- 可以使用
@Column(name = "fieldName")
指定不符合第3条规则的字段名- 使用
@Transient
注解可以忽略字段,添加该注解的字段不会作为表字段使用.- 建议一定是有一个
@Id
注解作为主键的字段,可以有多个@Id
注解的字段作为联合主键.默认情况下,实体类中如果不存在包含@Id
注解的字段,所有的字段都会作为主键字段进行使用(这种效率极低).- 由于基本类型,如int作为实体类字段时会有默认值0,而且无法消除,所以实体类中建议不要使用基本类型.
@GeneratedValue,
通用Mapper还提供了序列(支持Oracle)、UUID(任意数据库,字段长度32)、主键自增(类似Mysql,Hsqldb)三种方式,其中序列和UUID可以配置多个,主键自增只能配置一个。1.
@GeneratedValue(generator = "JDBC"):
MySql自增主键2.@GeneratedValue(generator = "UUID"):随机主键,可以用于任意字符串类型长度超过32位的字段
@NameStyle
注解,用来配置对象名/字段和表名/字段之间的转换方式,该注解优先于全局配置style
,可选值:
normal
:使用实体类名/属性名作为表名/字段名camelhump
:这是默认值,驼峰转换为下划线形式uppercase
:转换为大写lowercase
:转换为小写通过使用Mapper专用的MyBatis生成器插件可以直接生成符合要求带注解的实体类。
重点强调
@Transient
注解:如果你的实体类中包含了不是数据库表中的字段,你需要给这个字段加上
@Transient
注解,这样通用Mapper在处理单表操作时就不会将标注的属性当成表字段处理!
5.2 编写自定义mapper层代码
package com.cloudsw.mapper;
import com.cloudsw.base.IBaseMapper;
import com.cloudsw.dto.MsgTodo;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* 待阅信息表 Mapper 接口
* </p>
*
* @author zhenlong.qu@cloudsw.cn
* @since 2020-08-10
*/
@Mapper //编写mapper层接口,继承实现通用Mapper
public interface MsgTodoDao extends IBaseMapper<MsgTodo> {
}
5.3 编写Service层代码
接口类:
package com.cloudsw.service;
import com.cloudsw.base.IBaseService;
import com.cloudsw.dto.MsgTodo;
import java.util.List;
public interface IMsgTodoService{
int deleteByPrimaryKey(Integer id);
int insert(MsgTodo msgTodo);
MsgTodo selectByPrimaryKey(Integer id);
List<MsgTodo> selectAll();
int updateByPrimaryKey(MsgTodo msgTodo);
/**
* 根据条件分页查询
*
* @param country
* @param page
* @param rows
* @return
*/
List<MsgTodo> selectByMsgTodo(MsgTodo country, int page, int rows);
实现类:
package com.cloudsw.service.impl;
import com.cloudsw.base.impl.IBaseServiceImpl;
import com.cloudsw.dto.MsgTodo;
import com.cloudsw.mapper.MsgTodoDao;
import com.cloudsw.service.IMsgTodoService;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;
import javax.annotation.Resource;
import java.util.List;
@Service
public class ManagerServiceImpl extends IBaseServiceImpl<MsgTodo> implements IMsgTodoService {
@Autowired
private MsgTodoDao msgTodoDao;
@Override
public int deleteByPrimaryKey(Integer id) {
int row = msgTodoDao.deleteByPrimaryKey(id);
return row;
}
@Override
public int insert(MsgTodo msgTodo) {
return msgTodoDao.insert(msgTodo);
}
@Override
public MsgTodo selectByPrimaryKey(Integer id) {
return msgTodoDao.selectByPrimaryKey(id);
}
@Override
public List<MsgTodo> selectAll() {
return msgTodoDao.selectAll();
}
@Override
public int updateByPrimaryKey(MsgTodo msgTodo) {
return msgTodoDao.updateByPrimaryKey(msgTodo);
}
@Override
public List<MsgTodo> selectByMsgTodo(MsgTodo msgTodo, int page, int rows) {
Example example = new Example(MsgTodo.class);
Example.Criteria criteria = example.createCriteria();
if (msgTodo.getId() != null) {
criteria.andEqualTo("id", msgTodo.getId());
}
//分页查询
PageHelper.startPage(page, rows);
return selectByExample(example);
}
}
5.4 controller层代码:
package com.cloudsw.controller;
import com.cloudsw.dto.MsgTodo;
import com.cloudsw.service.IMsgTodoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
public class MsgTodoController {
@Autowired
private IMsgTodoService iMsgTodoService;
@PostMapping(value = "/msg/insert")
public int insert( @RequestBody MsgTodo msgTodo) {
return iMsgTodoService.save(msgTodo);
}
@DeleteMapping(value = "/msg/deleteByPrimaryKey")
public int deleteByPrimaryKey(@RequestParam(value = "id", required = true) Integer id) {
int row = iMsgTodoService.deleteByKey(id);
return row;
}
@PostMapping(value = "/manager/updateByPrimaryKey")
public int updateByPrimaryKey(@RequestBody MsgTodo msgTodo) {
return iMsgTodoService.updateAll(msgTodo);
}
@PostMapping(value = "/msg/selectByPrimaryKey")
public MsgTodo selectByPrimaryKey(@RequestParam(value = "id", required = true) Integer id) {
return iMsgTodoService.selectByKey(id);
}
@GetMapping(value = "/msg/selectAll")
public List<MsgTodo> selectAll() {
List<MsgTodo> managerList = iMsgTodoService.selectByExample(null);
return managerList;
}
@PostMapping (value = "/msg/selectAllByPage")
public List<MsgTodo> selectAllByPage( @RequestParam(required = false, defaultValue = "1") int page,
@RequestParam(required = false, defaultValue = "10") int rows,
@RequestBody MsgTodo msgTodo) {
List<MsgTodo> managerList = iMsgTodoService.selectByMsgTodo(msgTodo,page,rows);
return managerList;
}
}
通用Mapper中的常用方法:
查询相关: List<T> select(T record); //根据实体中的属性值进行查询,查询条件使用等号
T selectByPrimaryKey(Object key); //根据主键字段进行查询,方法参数必须包含完整的主键属性,查询条件使用等号
List<T> selectAll(); //查询全部结果,select(null)方法能达到同样的效果
T selectOne(T record); //根据实体中的属性进行查询,只能有一个返回值,有多个结果是抛出异常,查询条件使用等号
int selectCount(T record); //根据实体中的属性查询总数,查询条件使用等号
PageHelper.startPage(page, rows); //分页查询
添加相关: int insert(T record); //保存一个实体,null的属性也会保存,不会使用数据库默认值
int insertSelective(T record); //保存一个实体,null的属性不会保存,会使用数据库默认值
修改相关: int updateByPrimaryKey(T record); //根据主键更新实体全部字段,null值会被更新
int updateByPrimaryKeySelective(T record); //根据主键更新属性不为null的值
删除相关: int delete(T record); //根据实体属性作为条件进行删除,查询条件使用等号
int deleteByPrimaryKey(Object key); //根据主键字段进行删除,方法参数必须包含完整的主键属性
Example相关: List<T> selectByExample(Object example); //根据Example条件进行查询,这个查询支持通过Example类指定查询列,通过selectProperties方法指定查询列
int selectCountByExample(Object example); //根据Example条件进行查询总数
int updateByExample(@Param("record") T record, @Param("example") Object example); //根据Example条件更新实体record包含的全部属性,null值会被更新
int updateByExampleSelective(@Param("record") T record, @Param("example") Object example); //根据Example条件更新实体record包含的不是null的属性值
int deleteByExample(Object example); //根据Example条件删除数据
Example使用方法:
Example example = new Example(需要CRUD的类(与数据库对应实体类).class);
Example.Criteria criteria = example.createCriteria();
criteria.andEqualTo("id",id); // id已知 RUD判断条件
设置条件:example.orderBy(字段名).asc(); // 添加升序排列条件
example.orderBy(字段名).desc(); // 添加降序排序条件
example. setDistinct(false); // 去除重复,boolean型,true为选择不重复的记录
criteria. andIsNull(字段名); // 添加字段xx为null的条件
criteria. andIsNotNull(字段名); // 添加字段xx不为null的条件
criteria.andEqualTo(字段名,value); // 添加xx字段等于value条件
criteria.andNotEqualTo(字段名,value); // 添加xx字段不等于value条件
criteria.andGreaterThan(字段名,value); // 添加xx字段大于value条件
criteria.andGreaterThanOrEqualTo(字段名,value); // 添加xx字段大于等于vaue条件
criteria.andLessThan(字段名,value); // 添加xx字段小于value条件
criteria.andLessThanOrEqualTo(字段名,value); // 添加xx字段小于等于value条件
criteria.andIn(字段名,list); // 添加xx字段值在List条件
criteria. andNotIn(字段名,list); // 添加xx字段值不在List条件
criteria.andLike(字段名,“%”+value+”%”); // 添加xx字段值为vaue的模糊查询条件
criteria.andNotLike(字段名,“%”+value+”%”); // 添加xx字段值不为 value的模糊查询条件
criteria.andBetween(字段名,value1,value2); // 添加xx字段值在value1和value2之间条件
criteria.andNotBetween(字段名,value1,value2); // 添加xx字段值不在value1和value2之间条件