通常为了方便跳转到对应的xml文件,可安装 mybatisX插件
一、环境准备
pom.xml依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--属性校验-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper-spring-boot-starter.version}</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.1-jre</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>${hutool-core.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- swagger -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${swagger-knife4j.version}</version>
</dependency>
<!-- swagger -->
项目配置文件application.yml
server:
port: 7071
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
druid:
one:
max-active: 60 #最大连接数
min-idle: 5 #最小连接数
max-wait: 50000 #超时时间(ms)
mybatis:
# 实体类起存放包名
type-aliases-package: com.mybatis.demo
# 接口的配置文件的位置
mapper-locations: classpath:com/mybatis/demo/dao/../*.xml
二、项目结构
三、数据库建表初始化数据
drop table if exists t_user;
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL COMMENT '姓名',
`age` int(3) DEFAULT NULL COMMENT '年龄',
`status` tinyint(1) DEFAULT 0 COMMENT '状态 0-启用 1-禁用',
`create_by` varchar(32) DEFAULT NULL,
`create_date` bigint(13) DEFAULT NULL,
`update_by` varchar (32) DEFAULT NULL,
`update_date` bigint(13) DEFAULT NULL,
`is_delete` tinyint(1) DEFAULT NULL,
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `mybatis`.`t_user` (`name`, `age`, `status`, `create_by`, `create_date`, `update_by`, `update_date`, `is_delete`)
VALUES ('wangwu', 25, 0, 1, 1, NULL1 1, 1);
INSERT INTO `mybatis`.`t_user` (`name`, `age`, `status`, `create_by`, `create_date`, `update_by`, `update_date`, `is_delete`)
VALUES ('laoliu', 24, 0, 1, 1, 1, 1, 1);
INSERT INTO `mybatis`.`t_user` (`name`, `age`, `status`, `create_by`, `create_date`, `update_by`, `update_date`, `is_delete`)
VALUES ('liudehua', 58, 0, 1, 1, 1, 1, 1);
INSERT INTO `mybatis`.`t_user` (`name`, `age`, `status`, `create_by`, `create_date`, `update_by`, `update_date`, `is_delete`)
VALUES ('wuyifan', 39, 0, 1, 1, 1, 1, 1);
INSERT INTO `mybatis`.`t_user` (`name`, `age`, `status`, `create_by`, `create_date`, `update_by`, `update_date`, `is_delete`)
VALUES ('wangyifei', 43, 0, 1, 1, 1, 1, 1);
INSERT INTO `mybatis`.`t_user` (`name`, `age`, `status`, `create_by`, `create_date`, `update_by`, `update_date`, `is_delete`)
VALUES ('bagua', 36, 0, 1, 1, 1, 1, 1);
INSERT INTO `mybatis`.`t_user` (`name`, `age`, `status`, `create_by`, `create_date`, `update_by`, `update_date`, `is_delete`)
VALUES ('luoyonghao', 56, 0, 1, 1, 1, 1, 1);
INSERT INTO `mybatis`.`t_user` (`name`, `age`, `status`, `create_by`, `create_date`, `update_by`, `update_date`, `is_delete`)
VALUES ('qinwang', 63, 0, 1, 1, 1, 1, 1);
INSERT INTO `mybatis`.`t_user` (`name`, `age`, `status`, `create_by`, `create_date`, `update_by`, `update_date`, `is_delete`)
VALUES ('liuao', 29, 0, 1, 1, 1, 1, 1);
INSERT INTO `mybatis`.`t_user` (`name`, `age`, `status`, `create_by`, `create_date`, `update_by`, `update_date`, `is_delete`)
VALUES ('laowang', 36, 0, 1, 1, 1, 1, 1);
实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@JsonInclude(JsonInclude.Include.NON_NULL) # 字段值为null时不返回
public class User {
private Integer id;
private String name;
private Integer age;
private Boolean status;
private String createBy;
private Long createDate;
private String updateBy;
private Long updateDate;
private Boolean isDelete;
}
四、业务逻辑CRUD
4.1、根据ID查询
UserController.java
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
@GetMapping("/getById")
public JsonResult getById(@RequestParam("id") Integer id){
return ResultEnum.SUCCESS.jsonResult(userService.getById(id));
}
}
UserService.java
@Component
public interface {
User getById(Integer id);
}
UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserDao userDao;
@Override
public User getById(Integer id) {
return userDao.getById(id);
}
UseDao.java
@Mapper
public interface UserDao {
User getById(@Param("id") Integer id);
}
UserDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.demo.dao.user.UserDao">
<select id="getById" resultType="user" parameterType="int">
select * from t_user where id = #{id}
</select>
</mapper>
启动项目,调用接口
注意:这里返回的数据都没有驼峰命名的字段,原因是数据库都是下划线分割的
想要解决驼峰命名无法封装数据的问题,有两种方案:
4.1.1、起别名方式解决
从结果日志可以看出, updateBy 等字段没有封装成功,时因为从数据哭中取到的字段为update_by,在实体类中不存在该字段,解决方案就是起别名让字段对应即可
<select id="getById" resultType="user" parameterType="int">
select id, name, age, status, create_by as createBy, create_date as createDate,
update_by as updateBy, update_date as updateDate from t_user where id = #{id}
</select>
以上代码写一次尚可,如果频繁读取该类表字段,重复代码较多,Mybatis提供了sql片段
提高sql的复用
aql片段
<sql id="user_column">
id, name, age, status, create_by as createBy, create_date as createDate,update_by as updateBy, update_date as updateDate
</sql>
修改sql语句
<select id="getById" resultType="user" parameterType="int">
select <include refid="user_column" /> from t_user where id = #{id}
</select>
4.1.2、resultMap方式解决(推荐使用)
定义数据库字段与实体类属性名称的映射关系
<resultMap id="base_result_map" type="com.mybatis.demo.entity.user.User">
<!--<result column="数据库字段名称" property="实体类属性"/>-->
<result column="create_by" property="createBy"/>
<result column="create_date" property="createDate"/>
<result column="update_by" property="updateBy"/>
<result column="update_date" property="updateDate"/>
<result column="is_delete" property="isDelete"/>
</resultMap>
修改sql语句
<select id="getById" resultMap="base_result_map" parameterType="int">
select * from t_user where id = #{id};
</select>
嵌套 sql 片段使用
<sql id="user_col_for_detail">
id, name, age, status
</sql>
<select id="getById" resultMap="base_result_map" parameterType="int">
select <include refid="user_col_for_detail" /> from t_user where id = #{id};
</select>
4.1.3、参数占位符
mybatis 提供了两种参数占位符
-
#{} :执行SQL时,会将
#{}
占位符替换为?
,将来自动设置参数值,底层使用的是 PreparedStatement,不存在SQL注入 -
${} :拼接SQL,底层使用statement,会存在SQL注入问题
4.1.4、parameterType使用
4.1.5 SQL语句中特殊字段处理
4.2 多条件查询
Controller
@GetMapping("/getByManyCondition")
public JsonResult getByManyCondition(@RequestParam("keyword") String keyword,
@RequestParam(value = "status", defaultValue = "0") Integer status){
return ResultEnum.SUCCESS.jsonResult(userService.getByManyCondition(keyword, status));
}
Service
List<User> getByManyCondition(String keyword, Integer status);
ServiceImpl
public List<User> getByManyCondition(String keyword, Integer status){
return userDao.getByManyCondition(keyword, status);
}
Dao
List<User> getByManyCondition(@Param("keyword") String keyword, @Param("status") Integer status);
Dao.xml(Mapper.xml文件)
<select id="getByManyCondition" resultMap="base_result_map">
select * from `t_user`
<where>
<if test="keyword != null and keyword != ''">
`name` like concat('%', #{keyword}, '%')
</if>
<if test="status != null">
and `status` = #{status}
</if>
</where>
</select>
4.3 in
新增一个 t_user_detail 表,并创建对应的实体类 UserDetail
drop table if exists t_user_detail;
CREATE TABLE `t_user_detail` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(3) NOT NULL COMMENT '用户ID',
`address` varchar(64) DEFAULT NULL COMMENT '地址',
`mobile` varchar(16) DEFAULT NULL COMMENT '手机号',
`create_by` varchar(32) DEFAULT NULL,
`create_date` bigint(13) DEFAULT NULL,
`update_by` varchar (32) DEFAULT NULL,
`update_date` bigint(13) DEFAULT NULL,
`is_delete` tinyint(1) DEFAULT NULL,
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `mybatis`.`t_user_detail` (`user_id`, `address`, `mobile`, `create_by`, `create_date`, `update_by`, `update_date`, `is_delete`)
VALUES (1, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
INSERT INTO `mybatis`.`t_user_detail` (`user_id`, `address`, `mobile`, `create_by`, `create_date`, `update_by`, `update_date`, `is_delete`)
VALUES (2, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
INSERT INTO `mybatis`.`t_user_detail` (`user_id`, `address`, `mobile`, `create_by`, `create_date`, `update_by`, `update_date`, `is_delete`)
VALUES (3, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
INSERT INTO `mybatis`.`t_user_detail` (`user_id`, `address`, `mobile`, `create_by`, `create_date`, `update_by`, `update_date`, `is_delete`)
VALUES (4, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
Mybatis 实现 in 条件查询有三种实现方法($, List,[])
第一种方法:in 条件为拼接好的字符串
如果直接传入拼接好的where in 条件, 比如(‘111’,‘222’,‘333’),则需要使用${idList}传参,即绝对引用,而不能使用#, 如果使用#传参会被mybatis当成字符串再添加一层’'引号,导致错误.
优点:简单方便,高效,缺点:不能防止SQL注入
第二种方法:in 条件为List对象
in条件直接传入List对象,让mybatis再去拼接生成in条件,这个很麻烦,但是可以防止SQL注入
第三种方法:in 条件为String[] 数组
in条件直接传入[]数组对象,让mybatis再去拼接生成in条件,这个很麻烦,但是可以防止SQL注入
如果项目大,其实可以同时重载三种都实现,我一般都会这样,实现三种DAO接口,service层相同方法名,根据不同的模块不同的需求调用不同的实现层
Controller
@GetMapping("/del1")
public JsonResult delUser1(@RequestParam("idList") String idList){
System.out.println(idList);
return ResultEnum.SUCCESS.jsonResult(userService.deleteUserByIdList(idList, 0, 0));
}
@GetMapping("/del2")
public JsonResult delUser2(@RequestParam List<String> idList){
System.out.println(idList);
return ResultEnum.SUCCESS.jsonResult(userService.deleteUserByIdList(idList, 0, 0));
}
@GetMapping("/del3")
public JsonResult delUser3(@RequestParam String[] idList){
System.out.println(idList);
return ResultEnum.SUCCESS.jsonResult(userService.deleteUserByIdList(idList, 0, 0));
}
Service:
int deleteUserByIdList(String idList,int delCount,int lastSort);
int deleteUserByIdList(List<String> idList, int delCount,int lastSort);
int deleteUserByIdList(String[] idList, int delCount,int lastSort);
Dao
//用这种写法方便,idList 直接拼接好,xml中用 in ${idList}接受参数
int deleteUserByIdList1(@Param("idList")String idList, @Param("delCount")int delCount, @Param("lastSort")int lastSort);
//用这种写法直接传List对象,xml中再写循环拼接,麻烦
int deleteUserByIdList2(@Param("idList")List<String> idList, @Param("delCount")int delCount, @Param("lastSort")int lastSort);
//用这种写法直接传String[]数组,xml中再写循环拼接,麻烦
int deleteUserByIdList3(@Param("idList")String[] idList, @Param("delCount")int delCount, @Param("lastSort")int lastSort);
Dao.xml(Mapper.xml文件)
<delete id="deleteUserByIdList1" >
update t_user set is_delete = 1 where id in ${idList};
</delete>
<delete id="deleteUserByIdList2" >
update t_user set is_delete = 1 where id in
<foreach collection="idList" item="id" separator="," open="(" close=")">
${id}
</foreach>;
</delete>
<delete id="deleteUserByIdList3" >
update t_user set is_delete = 1 where id in
<foreach collection="idList" item="id" separator="," open="(" close=")">
${id}
</foreach>;
</delete>
4.4 mybatis插入后返回主键id的三种方式
mybatis 操作后默认返回受影响的条数,如插入数据后想返回插入数据的id,则需要自行配置,方法有下列三种
第一种:在xml文件,insert标签属性中,添加useGeneratedKeys = true和keyProperty=id
<insert id="addUser" parameterType="com.mybatis.demo.entity.user.User" useGeneratedKeys="true" keyProperty="id">
insert into t_user (`name`, `age`, `status`)
values (#{user.name}, #{user.age}, #{user.status});
</insert>
第二种:在xml文件,添加selectKey标签属性
各个属性值含义:
resultType:查询结果的类型;
keyProperty:把查询的值赋给谁;
order:在插入前还是后执行,id在insert语句插入之后才会生成id,所以要在插入之后执行,所以此处order=after;
<insert id="addUser" parameterType="com.mybatis.demo.entity.user.User">
<selectKey keyProperty="id" resultType="integer" order="AFTER">
select LAST_INSERT_ID()
</selectKey>
insert into t_user (`name`, `age`, `status`)
values (#{user.name}, #{user.age}, #{user.status});
</insert>
第三种:在dao层用注解的方式,用@SelectKey注解
注解里面属性含义:
resultType:查询结果的类型,
keyProperty:把查询的值赋给谁;
statement:查找最后一个插入的id;
keyColumn:查询的是哪一列;
before:是否在插入之前执行, id在insert语句插入之后才会生成id,所以要在插入之后执行,所以此处before=false;
Dao
@SelectKey(statement = "select last_insert_id()", before = false, resultType = Integer.class, keyColumn = "id", keyProperty = "id")
@Insert("insert into t_user (`name`, `age`, `status`) values (#{user.name}, #{user.age}, #{user.status})")
int addUser(@Param("user") User user);