一、简介
MyBatis它是一款优秀的持久层框架,它支持自定义SQL、存储过程及高级映射。不像Hibernate等一些全自动框架,对于关键的SQL部分可以交由程序自己编写而不是自动生成,从而提高了灵活性。
二、基础使用示例
基础依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.3</version>
</dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope>
</dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter-test</artifactId><version>3.0.3</version><scope>test</scope>
</dependency>
数据库及相关表数据
CREATE DATABASE `mybatis-example`;USE `mybatis-example`;CREATE TABLE `t_emp`(id INT AUTO_INCREMENT,emp_name CHAR(100),age INT,emp_salary DOUBLE(10,5),PRIMARY KEY(id)
);INSERT INTO `t_emp`(emp_name,age,emp_salary) VALUES("tom",18,200.33);
INSERT INTO `t_emp`(emp_name,age,emp_salary) VALUES("jerry",19,666.66);
INSERT INTO `t_emp`(emp_name,age,emp_salary) VALUES("andy",20,777.77);
基础配置
我们需要操作数据库至少要配置数据库连接的url,账号,密码信息
# 数据库配置信息
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis-example
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
对于数据表的JavaBean
@Data
public class Emp {private Integer id;private String empName;private Integer age;private Double empSalary;
}
操作数据库的mapper接口
@Mapper
public interface EmpMapper {Emp getEmpById(Integer id);
}
我们使用MyBatis后,对于这个mapper接口的实现我们不再写了,而我们一般为每个mapper接口提供一个xml文件,在这个文件中配置指定的sql。
注意:接口上的注解@Mapper是MyBatis提供的!!
sql映射文件
<?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.xiaoxie.mybatis.dao.EmpMapper"></mapper>
这个文件可以借助相关的插件来生成。
其中namespace属性指定了接口的全类名,这样的话这个sql映射文件就与具体的接口进行了关联。
我们要在映射文件中实现接口中对应方法的sql,最终如下:
<?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" >
<!-- namespace写mapper接口的全类名,代表这个xml文件与这个指定的mapper接口绑定 -->
<mapper namespace="com.xiaoxie.mybatis.dao.EmpMapper"><!-- Emp getEmpById(Integer id); --><!--select标签代表一次查询id:表示对应绑定的mapper接口中的方法名resultType:表示查询的结果类型sql语句中写了字段别名是为了保证查询结果的列名与我们实体的名称保持一致--><select id="getEmpById" resultType="com.xiaoxie.mybatis.bean.Emp">selectid,emp_name as empName,age,emp_salary as empSalaryfrom t_emp where id = #{id}</select>
</mapper>
配置文件中指定sql映射文件位置
# mybatis配置
# xml的sql映射文件位置
mybatis.mapper-locations=classpath:mapper/**.xml
以上步骤则基本的集成使用就完成,其实现步骤总结如下:
- 导入mybatis相关依赖
- 配置数据源的连接信息,这一步与使用不使用mybaits无关,要操作数据库都是需要的
- 编写JavaBean对应数据库的表模型
- 编写mapper接口,用它来对应一个sql映射文件,注意:在mybaits当中mapper接口使用@Mapper注解标注而不是Spring中的@Repository
- 在classpath路径下新建sql映射文件,这个映射文件最终与mapper接口对应
- 在配置文件中配置sql映射文件的位置
基础搭建细节说明
- 每个mapper接口要对应一个sql映射文件
- mapper接口的实现类由是一个由MyBatis自动生成的代理类来实现的,容器中mybatis为每个mapper接口会创建动态代理的实现
- sql映射文件中namespace需要绑定mapper接口的全类名
- 在sql映射文件中select,insert,update,delete标签分别对应的是要做的查询,新增,更新,删除动作。
- 标签中的id属性要与mapper接口中的方法名对应上
- 如果方法有返回值必须在标签中指定resultType,其指定的类型要与返回值匹配
- 标签体中则是具体要执行的sql
- 对于复杂的结果封装,需要指定resultMap规则
添加运行sql日志打印
在配置文件中添加如下配置
# 日志配置
# 指定具体包名日志级别
logging.level.com.xiaoxie.mybatis.mapper=debug
新增时获取数据库自增id值
注意,当我们新增数据后,不应该依赖新增完后再做查询来获取,因为这种方式对于单线程下查可能是没有问题的,多线程下我们是不知道具体要查哪一条记录的(除非我们新增的数据一定会有一字段保持记录的唯一性),如果我们只是查ID最大值这个时候可能有别人写进来数据,这时查的就错了!!
如果我们要获取新增成功的id,可以按如下操作:
- 在sql映射文件中的相关insert标签中新增一个属性:useGeneratedKeys="true"
- 这个表示告诉mybatis,这个新增语句会使用自动生成的主键
- 再在这个标签中新增一个属性:keyProperty="id"
- 表示自动生成的主键值对应的JavaBean属性值是id,这样的话最终自动把自动生成的主键值封装到JavaBean的id属性上
开启驼峰命名
我们一般在数据库中使用下划线_来分隔字段名的多个部分,而在JavaBean属性中使用驼峰命名,这样就会导致我们数据库的字段名与JavaBean的属性名对应不上,对于这种对应不上我们前面的解决方式是在sql映射文件中使用别名的方式,但是写别名还是比较麻烦的,所以mybatis提供了一个配置可以完成_转驼峰的自动映射
# 开启驼峰命名自动映射
mybatis.configuration.map-underscore-to-camel-case=true
三、关于mybaits的参数传递
在前面基础使用示例中查询我们传入的id参数我们使用的是#{}的方式来占位表示的,其实还有${}。
#{}与${}
#{}
底层使用的是PreparedStatement方式,SQL预编译后设置参数,无sql注入攻击风险
${}
底层使用statement方式,SQL没有预编译,直接拼接参数,它是会有sql注入风险的
使用建议:所有参数尽量都使用#{},对于动态表名的位置才用${},因为表名那个地方是没有办法通过#{}使用占位符赋值进去的
注意:但凡是使用了${}的业务,一定要注意SQL注入风险,要自编写防SQL注入攻击代码!
#{}单个参数取值
单个参数 - 普通类型
取值的方式:#{变量名}
示例如下:
Emp getEmp(Integer id);
<!-- 当只有一个参数的时候这里#{}中不管写什么都是表示取那个唯一参数的值,建议写的时候保持与参数名一致 -->
<select id="getEmp" resultType="com.xiaoxie.mybatis.bean.Emp">select * from t_emp where id = #{abc}
</select>
单个参数 - List类型
取值方式:#{变量名[0]}
示例如下:
Emp getEmp02(List<Integer> ids);
<select id="getEmp02" resultType="com.xiaoxie.mybatis.bean.Emp">select * from t_emp where id = #{ids[1]}
</select>
单个参数 - 对象类型
取值方式:#{对象中的属性名}
示例如下:
void addEmp(Emp emp);
<insert id="addEmp">insert into t_emp(emp_name,age,emp_salary) values(#{empName},#{age},#{empSalary})
</insert>
单个参数 - Map类型
取值方式:#{map中的key}
示例如下:
void addEmp02(Map<String, Object> m);
<insert id="addEmp02">insert into t_emp(emp_name,age,emp_salary) values(#{name},#{age},#{salary})
</insert>
多参数@Param注解每个参数名字
在新版的MyBatis中是支持多个参数的方法直接在sql映射文件中使用#{参数名}来取得对应的参数值的。但是这个特性在老版本中是不支持的,需要在方法参数中使用@Param注解告诉MyBatis参数的名称是什么
如下所示:
Emp getEmpByIdAndName(@Param("id") Integer id, @Param("empName") String empName);
<!--新版本中支持多个参数情况下,直接使用#{参数名}以上特性在老版本中不支持,,需要在方法参数上使用@Param注解告诉Mybatis,参数名称-->
<select id="getEmpByIdAndName" resultType="com.xiaoxie.mybatis.bean.Emp">select * from t_emp where id = #{id} and emp_name = #{empName}
</select>
建议在多参数时推荐使用@Param注解指定参数名。原因如下:
- 直接使用参数名这个特性必须要升级使用新版本Mybatis
- 使用@Param注解指定参数名这样使用时会更加明确
示例如下:
Emp getEmpTest(@Param("id") Integer id,@Param("m") Map<String,Object> m,@Param("ids") List<Integer> ids,@Param("e") Emp e);
<select id="getEmpTest" resultType="com.xiaoxie.mybatis.bean.Emp">select * from t_emp where id=#{id}and emp_name = #{m.name}and age=#