概述
MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索,底层是封装的JDBC。
作用:
- 简化JDBC的开发
- 自动完成ORM(对象关系映射)
核心资源
- 核心配置文件
mybatis-config.xml,配置了事务管理与数据源。
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><!-- 配置别名 --><typeAliases><typeAlias type="cn.tedu.pojo.User" alias="User"/><typeAlias type="cn.tedu.pojo.Dept" alias="Dept"/></typeAliases><!-- environments可以配置多个数据库的连接信息,default指定环境,当前默认为test环境 --><environments default="test"><environment id="test"><!-- 使用的事务管理器为JDBC --><transactionManager type="JDBC"/><!-- 连接数据库的配置信息,配置数据源 --><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url"value="jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&serverTimezone=Asia/Shanghai"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments><mappers><mapper resource="mappers/UserMapper.xml"/><mapper resource="mappers/DeptMapper.xml"/></mappers></configuration>
- 映射文件
nameMapper.xml,存放大量的CRUD的SQL语句。
<?xml version="1.0" encoding="UTF-8"?><!--此文件为User的映射文件--><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace为映射文件的唯一标识--><mapper namespace="userMapper"><!-- 查询id为为1的用户信息,id为sql语句的唯一标识,resultType="类的全路径",用于封装查询到的结果--><select id="getById" resultType="cn.tedu.pojo.User">select *from userwhere id = 1</select><!-- 查询所有用户信息 --><select id="getInfo" resultType="cn.tedu.pojo.User">select *from user</select><!-- 根据用户名查询指定用户信息SQl动态获取参数时,可以使用#{}或${}#{}底层使用了高级传输器,安全高效,会自动进行字符串拼接${}底层使用了低级传输器,会发生SQL注入--><!-- User为别名,在配置文件中设置 --><select id="getByName" resultType="User">select *from userwhere name = #{name}</select><!-- <insert id="addUser" parameterType="User">--><!-- insert into user--><!-- values (null, #{name}, #{addr}, #{age})--><!-- </insert>--></mapper>
- 核心工具类
会话工厂:SqlSessionFactory:产生会话
会话SqlSession:执行SQL语句
package cn.tedu.test;import cn.tedu.pojo.Dept;import cn.tedu.pojo.User;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.jupiter.api.Test;import java.io.IOException;import java.io.InputStream;import java.util.List;public class TestMybatis {@Testpublic void get() throws IOException {//获取sqlSessionFactoryInputStream in = Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory session = new SqlSessionFactoryBuilder().build(in);//开启会话,获取SqlSession准备执行Sql语句SqlSession sqlSession = session.openSession();//查询id为1的用户信息User user = sqlSession.selectOne("userMapper.getById");Integer id = user.getId();String name = user.getName();String addr = user.getAddr();Integer age = user.getAge();System.out.println("id=" + id + ",name=" + name + ",addr=" + addr + ",age=" + age);//查询所有用户信息List<User> info = sqlSession.selectList("userMapper.getInfo");for (User u : info) {System.out.println(u);}//查询指定用户信息String userName = "hanmeimei";User user1 = sqlSession.selectOne("userMapper.getByName", userName);System.out.println(user1);// User user1 = new User();// user1.setName("马钊").setAddr("地府").setAge(-125);// sqlSession.insert("addUser",user1);// sqlSession.commit();//查询dept表中id为1的信息Dept d1 = sqlSession.selectOne("deptMapper.getById");System.out.println(d1);}}
- ORM
对象关系映射,将表中的字段与类中的属性进行映射,使用类中的属性保存的表中的字段值。
示例
准备数据
create database mybatisdb default character set utf8;use mybatisdb;create table user(id int primary key auto_increment,name varchar(100),addr varchar(100),age int);Insert into user values(null,'hanmeimei','北京',28);Insert into user values(null,'xiongda','上海',20);Insert into user values(null,'xiaonger','上海',19);DROP TABLE IF EXISTS `dept`;CREATE TABLE `dept` (`id` int(11) NOT NULL AUTO_INCREMENT,`dname` varchar(14) DEFAULT NULL,`loc` varchar(13) DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;-- ------------------------------ Records of dept-- ----------------------------INSERT INTO `dept` VALUES ('1', '呵呵呵', '一区');INSERT INTO `dept` VALUES ('2', '哈哈哈哈', '二区');INSERT INTO `dept` VALUES ('3', 'operations', '二区');INSERT INTO `dept` VALUES ('5', 'java教研部', '大钟寺');INSERT INTO `dept` VALUES ('10', '开发', '西二旗');DROP TABLE IF EXISTS `emp`;CREATE TABLE `emp` (`id` int(11) NOT NULL AUTO_INCREMENT,`ename` varchar(10) DEFAULT NULL,`job` varchar(9) DEFAULT NULL,`mgr` decimal(4,0) DEFAULT NULL,`hiredate` date DEFAULT NULL,`sal` decimal(7,2) DEFAULT NULL,`comm` decimal(7,2) DEFAULT NULL,`deptno` int(11) DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=510 DEFAULT CHARSET=utf8;-- ------------------------------ Records of emp-- ----------------------------INSERT INTO `emp` VALUES ('100', 'jack', '副总', null, '2002-05-03', '90000.00', null, '1');INSERT INTO `emp` VALUES ('200', 'tony', '总监', '100', '2015-02-02', '10000.00', '2000.00', '2');INSERT INTO `emp` VALUES ('300', 'hana', '经理', '200', '2017-02-02', '8000.00', '1000.00', '2');INSERT INTO `emp` VALUES ('400', 'leo', '员工', '300', '2019-02-22', '3000.00', '200.12', '2');INSERT INTO `emp` VALUES ('500', 'liu', '员工', '300', '2019-03-19', '3500.00', '200.58', '2');INSERT INTO `emp` VALUES ('502', '王一博', 'topidol.', '1000', '2021-03-31', '20000.00', '99.00', '88');INSERT INTO `emp` VALUES ('504', '蔡徐坤', 'rapper', '10', '2021-03-29', '100.00', '1000.00', '100');
创建核心配置文件
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><!-- environments可以配置多个数据库的连接信息,default指定环境,当前默认为test环境 --><environments default="test"><environment id="test"><!-- 使用的事务管理器为JDBC --><transactionManager type="JDBC"/><!-- 连接数据库的配置信息,配置数据源 --><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url"value="jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&serverTimezone=Asia/Shanghai"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments></configuration>
创建映射文件
UserMapper.xml
映射语句需写在mapper双标签中,其中namespace是映射文件的唯一标识,在<mapper><mapper/>中可以编写SQL语句,每种语句具有自己的双标签,<select></select>标签中可以编写查询语句,id为SQL语句的为一标识,resultType为类的全路径,封装查询到的结果。
<?xml version="1.0" encoding="UTF-8"?><!--此文件为User的映射文件--><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace为映射文件的唯一标识--><mapper namespace="userMapper"><!-- 查询id为为1的用户信息,id为sql语句的唯一标识,resultType="类的全路径",用于封装查询到的结果--><select id="getById" resultType="cn.tedu.pojo.User">select *from userwhere id = 1</select></mapper>
将映射文件注册到配置文件中
在映射文件创建完毕后,需要将映射文件注册到mybatis配置文件(mybtis-config.xml)中才可使其生效。注册代码为:
<mappers><mapper resource="mappers/UserMapper.xml"/></mappers>
<mappers></mappers>标签中注册需要进行注册的映射文件,resource中为映射文件相对于配置文件的相对路径。
测试
进行SQL操作需要使用SqlSessionFactory,因此需要实例化SqlSessionFactoryBuilder来构建SqlSessionFactory,使用输入流来进行数据传输,Resource中提供了各种工具类,使用其中的getResourceAsStream方法,参数为mybatis的配置文件路径。将输入流实例化后使用SqlSessionFactoryBuilder的方法build进行数据流传输。然后开启session会话执行SQL语句。sqlSession中有各种方法进行SQL语句的执行,使用时需要使用映射文件中的SQL语句的id来确定执行那一条语句。
package cn.tedu.test;import cn.tedu.pojo.User;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.jupiter.api.Test;import java.io.IOException;import java.io.InputStream;import java.util.List;public class TestMybatis {@Testpublic void get() throws IOException {//获取sqlSessionFactoryInputStream in = Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory session = new SqlSessionFactoryBuilder().build(in);//开启会话,获取SqlSession准备执行Sql语句SqlSession sqlSession = session.openSession();//查询id为1的用户信息User user = sqlSession.selectOne("userMapper.getById");Integer id = user.getId();String name = user.getName();String addr = user.getAddr();Integer age = user.getAge();System.out.println("id=" + id + ",name=" + name + ",addr=" + addr + ",age=" + age);}}
根据姓名查询user表中的用户信息
映射语句
{name}为传递的用户姓名。可在测试文件中自定义。
<select id="getByName" resultType="User">select *from userwhere name = #{name}</select>
进行查询selectOne方法参数的第一个参数为SQL语句的唯一标识,第二个参数为进行查询的姓名。
//查询指定用户信息String userName = "hanmeimei";User user1 = sqlSession.selectOne("userMapper.getByName", userName);System.out.println(user1);
别名
可以在mybatis的配置文件中为结果集起别名,配置代码如下:
<!-- 配置别名 --><typeAliases><typeAlias type="cn.tedu.pojo.User" alias="User"/></typeAliases>
在映射文件中使用别名
resultType即为别名。
<!-- User为别名,在配置文件中设置 --><select id="getByName" resultType="User">select *from userwhere name = #{name}</select>
#{}与$的区别
SQl动态获取参数时,可以使用#{}或${}
#{}底层使用了高级传输器,安全高效,会自动进行字符串拼接
${}底层使用了低级传输器,会发生SQL注入
接口开发
1.创建映射文件
<mapper></mapper>标签中的namespace为接口的全路径,SQL语句中的id为接口中的方法名,以此来进行映射文件与接口的一一对应。
<?xml version="1.0" encoding="UTF-8"?><!--此文件为User的映射文件--><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace的值为接口中的全路径--><mapper namespace="cn.tedu.dao.DeptMapper"><!-- id为接口中的方法名 --><select id="getById" resultType="Dept">select *from deptwhere id = 1</select></mapper>
2.创建接口
注意接口名与接口中的方法名与映射文件的配置进行对应。
package cn.tedu.dao;import cn.tedu.pojo.Dept;public interface DeptMapper {Dept getById();}
3.使用接口进行SQL操作
需要使用反射获取指定接口,然后调用接口中的方法,接口中的方法与映射文件的SQL语句对应,调用后将会返回映射文件中的结果集。
package cn.tedu.test;import cn.tedu.dao.DeptMapper;import cn.tedu.pojo.Dept;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.jupiter.api.Test;import java.io.IOException;import java.io.InputStream;public class TestInterface {@Testpublic void get() throws IOException {InputStream in = Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory session = new SqlSessionFactoryBuilder().build(in);SqlSession sqlSession = session.openSession();//使用反射获取指定接口DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);//调用接口中的方法,接口中的方法将会执行查询语句并返回结果集Dept d = mapper.getById();System.out.println(d);}}
4.在接口开发中使用参数
查询办公地址为2区的部门信息,将loc设为#{loc}
<select id="getByLoc" resultType="Dept">select *from deptwhere loc = #{loc};</select>
在接口中创建方法
因为映射文件中的地址需要参数,因此接口方法中需要设置参数。
package cn.tedu.dao;import cn.tedu.pojo.Dept;import java.util.List;public interface DeptMapper {Dept getById();List<Dept> getDnameInfo(Object dname);List<Dept> getByLoc(Object loc);}
调用接口方法并进行参数传递
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory session = new SqlSessionFactoryBuilder().build(in);SqlSession sqlSession = session.openSession();//使用反射获取指定接口DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);List<Dept> d3 = mapper.getByLoc("二区");System.out.println(d3);
动态SQL
sql和include
使用Sql标签可以提取SQL片段,来提高SQL的复用性,其中id为Sql标签的唯一标识,使用时需要使用include来指定引用片段,指定使用Sql标签需要使用refid属性来指定Sql的唯一标识id,以此来使用指定的Sql片段。
<?xml version="1.0" encoding="UTF-8"?><!--此文件为User的映射文件--><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace的值为接口中的全路径--><mapper namespace="cn.tedu.dao.DeptMapper"><sql id="cols">id,dname,loc</sql><!-- id为接口中的方法名 --><select id="getById" resultType="Dept">select<include refid="cols"/>from deptwhere id = 1</select></mapper>
if
在执行SQL时可以添加判断条件,只有当判断条件满足时,才会执行该SQL语句,使用test属性设置判断条件。
<?xml version="1.0" encoding="UTF-8"?><!--此文件为User的映射文件--><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace的值为接口中的全路径--><mapper namespace="cn.tedu.dao.DeptMapper"><sql id="cols">id,dname,loc</sql><select id="getDnameInfo" resultType="Dept">select<include refid="cols"/>from dept<!-- if条件不满足将不会执行if标签中的语句 --><if test="dname != null">where dname = #{dname};</if></select></mapper>
where
在执行SQL语句时,若if标签中有AND或OR语句,那么当if标签中的条件不生效时,执行该SQL语句将会报错。where可以避免这种情况。
<select id="find" resultType="Item" >SELECT <include refid="cols"/> FROM tb_item<where><if test="title != null"> title like #{title} </if><if test="sellPoint != null">and sell_point like #{sellPoint}</if></where></select>
foreach标签
MyBatis的foreach标签应用于多参数的交互如:多参数(相同参数)查询、循环插入数据等,foreach标签包含collection、item、open、close、index、separator,且常用于遍历集合构建in条件语句或批量操作语句。
| 属性 | 描述 |
|---|---|
| collection | 参数名称,根据Mapper接口的参数名确定,也可以使用@Param注解指定参数名,若为map集合,则为key-value中的key;该参数必选。 |
| item | 表示集合中每一个元素进行迭代时的别名,若collection为List、Set或者数组,则表示其中的元素,相当于for循环中的i;若collection为map,则代表key-value的value,该参数为必选 |
| open | 表示该语句以什么开始,最常用的是左括弧’(’,注意:mybatis会将该字符拼接到整体的sql语句之前,并且只拼接一次,该参数为可选项 |
| close | 表示该语句以什么结束,最常用的是右括弧’)’,注意:mybatis会将该字符拼接到整体的sql语句之后,该参数为可选项 |
| separator | 分隔符,每次循环完成后添加此分隔符;mybatis会在每次迭代后给sql语句append上separator属性指定的字符,该参数为可选项 |
| index | 在list、Set和数组中,index表示当前迭代的位置,在map中,index代指是元素的key,该参数是可选项。 |
特殊字符
XML文件中有些字符正常使用无法使用,因此需要转义,例如>, < , & , ‘ , “ 等字符。
若想正常使用有两种方式:
使用<![CDATA[“需要转义的语句”]],括号中为需要换一的语句
<![CDATA[and age<=#{age}]]>
使用特殊字符 | 普通字符 | 名称 | 转义后 | | —- | —- | —- | | < | 小于号 | < | | > | 大于号 | > | | & | 和 | & | | ‘ | 单引号 | ' | | “ | 双引号 | " |
ResultMap
概述
当数据库中字段名与pojo对象的属性名一致时可以使用resultType,因此resultType只能完成简单的ORM。
单数字段名与pojo对象中的属性名不一致时需要使用resultMap。
使用方法:
映射文件
<resultMap id="userInfoRM" type="cn.tedu.pojo.UserInfo"><result property="userName" column="user_name"/><result property="userAddr" column="user_addr"/><result property="userAge" column="user_age"/></resultMap>
pojo类
package cn.tedu.pojo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import lombok.experimental.Accessors;@Data@NoArgsConstructor@AllArgsConstructor@Accessors(chain = true)public class UserInfo {private Integer id;private String userName;private String userAddr;private String userAge;}
数据表
其中id为resultMap的唯一标识,type为pojo类的全路径,resultMap标签中将pojo类中的属性与数据表中的名称不同的字段进行一对一映射。property属性为pojo类的属性,column为数据表的字段名。
自动匹配规范驼峰规则
数据库中字段名一般全大写,多个单词之间使用下划线隔开,java中属性一般使用java驼峰规则,因此需要大量resultMap标签,可以开启驼峰规则自动映射,自动转换。
mapper配置中不需要写字段与属性的配置,会进行自动映射,但主键需要单独写,其他的可以使用驼峰规则自动映射。
在mybatis的核心配置文件Mybatis-config.xml开启驼峰规则,代码如下。
<settings><!-- 开启驼峰规则 --><setting name="mapUnderscoreToCamelCase" value="true"/></settings>
在映射文件中配置驼峰规则开启,使用autoMapping属性将其设置为true
<resultMap id="userInfoRM" type="cn.tedu.pojo.UserInfo" autoMapping="true"></resultMap>
参数传递
Mybatis参数传递只支持单值传参,多值传参需要进行封装,将多个值封装为单值。
封装方式有两种:
- 封装为对象
- 封装为Map集合
一般使用最多的为封装为对象,若不能封装为对象,则使用Map封装集合。
封装为对象传参
创建对象并通过set方法赋值,调用DAO接口时传递参数为封装的对象,Mybatis会自动根据传递的对象的值进行给SQL语句拼接。
测试类:
@SpringBootTestpublic class TestMybatis {@Autowiredprivate UserMapper userMapper;/*查询年龄为18且为男的数据,适用对象封装。*/@Testpublic void testMybatis() {User user = new User().setSex("男").setAge(18);List<User> users = userMapper.findWhere(user);System.out.println(users);}}
DAO:
package com.jt.dao;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.jt.pojo.User;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Param;import java.util.List;@Mapper/*继承BaseMapper接口必须添加泛型对象,否则映射表报错自己创建的接口不要与接口方法重名*/public interface UserMapper extends BaseMapper<User> {List<User> findWhere(User user);}
Mapper.XML
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.jt.dao.UserMapper"><select id="findWhere" resultType="com.jt.pojo.User">select *from demo_userwhere sex = #{sex}and age = #{age};</select></mapper>
封装为Map集合
将多个参数封装为Map集合需要使用@Param注解,例如:List<User> findAge(@Param("minAge") int minAge, @Param("maxAge") int maxAge);@Param注解参数为Map集合中的key也即参数名称,value为参数值。
测试类:
@SpringBootTestpublic class TestMybatis {@Autowiredprivate UserMapper userMapper;/*查询:age<18 or age>100*/@Testpublic void testMybatis2() {int minAge = 18;int maxAge = 100;List<User> users = userMapper.findAge(minAge, maxAge);for (User user : users) {System.out.println(user);}}}
DAO:
package com.jt.dao;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.jt.pojo.User;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Param;import java.util.List;@Mapper/*继承BaseMapper接口必须添加泛型对象,否则映射表报错自己创建的接口不要与接口方法重名*/public interface UserMapper extends BaseMapper<User> {/*mybatis原生只支持单值传参,多值传参需要封装为单值1.封装为对象2.封装为Map集合若为多值传参自动封装为Map集合key为参数名称,value为参数值*/List<User> findAge(@Param("minAge") int minAge, @Param("maxAge") int maxAge);}
Mapper.XML
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.jt.dao.UserMapper"><select id="findAge" resultType="com.jt.pojo.User">select *from demo_userwhere age < #{minAge}or age > #{maxAge};</select></mapper>
