概述
MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索,底层是封装的JDBC。
作用:
- 简化JDBC的开发
- 自动完成ORM(对象关系映射)
核心资源
- 核心配置文件
mybatis-config.xml,配置了事务管理与数据源。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//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 mapper
PUBLIC "-//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 user
where 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 user
where 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 {
@Test
public void get() throws IOException {
//获取sqlSessionFactory
InputStream 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 configuration
PUBLIC "-//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 mapper
PUBLIC "-//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 user
where 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 {
@Test
public void get() throws IOException {
//获取sqlSessionFactory
InputStream 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 user
where 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 user
where name = #{name}
</select>
#{}与$的区别
SQl动态获取参数时,可以使用#{}或${}
#{}底层使用了高级传输器,安全高效,会自动进行字符串拼接
${}底层使用了低级传输器,会发生SQL注入
接口开发
1.创建映射文件
<mapper></mapper>
标签中的namespace为接口的全路径,SQL语句中的id为接口中的方法名,以此来进行映射文件与接口的一一对应。
<?xml version="1.0" encoding="UTF-8"?>
<!--此文件为User的映射文件-->
<!DOCTYPE mapper
PUBLIC "-//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 dept
where 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 {
@Test
public 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 dept
where 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 mapper
PUBLIC "-//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 dept
where id = 1
</select>
</mapper>
if
在执行SQL时可以添加判断条件,只有当判断条件满足时,才会执行该SQL语句,使用test属性设置判断条件。
<?xml version="1.0" encoding="UTF-8"?>
<!--此文件为User的映射文件-->
<!DOCTYPE mapper
PUBLIC "-//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语句拼接。
测试类:
@SpringBootTest
public class TestMybatis {
@Autowired
private UserMapper userMapper;
/*
查询年龄为18且为男的数据,适用对象封装。
*/
@Test
public 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 mapper
PUBLIC "-//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_user
where 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为参数值。
测试类:
@SpringBootTest
public class TestMybatis {
@Autowired
private UserMapper userMapper;
/*
查询:age<18 or age>100
*/
@Test
public 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 mapper
PUBLIC "-//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_user
where age < #{minAge}
or age > #{maxAge};
</select>
</mapper>