官网地址: https://mybatis.org/mybatis-3/zh/index.html
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
MyBatis 本是apache的一个开源项目iBatis.
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.2.0version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
持久层:POJO对象 数据库表
1.数据表:用户的数据信息,持久化到本地磁盘
2.POJO:程序通过对象封装数据信息
映射关系: 一个POJO对象要求映射一张数据表
1.对象名称 映射数据表表名
2.对象的属性 映射数据表中的字段
Mybatis方法定义四类:
List findall(); 返回多个值
User findAll2(); 返回一个值
int update(): 影响的行数
void update2(); 无返回值
编辑POJO对象:
配置
文件
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/jt?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="mybatis/mappers/UserMapper.xml"/>
mappers>
configuration>
public interface UserMapper {
//指定接口方法 查询demo_user所有数据
List<User> findAll();
}
映射
文件说明:由于mybatis需要操作数据库,编辑Sql语句,采用xml映射文件维护Sql语句
**关系
:
1.一个接口
对应一个xml的映射文件
2.一个接口方法
对应一个映射文件的Sql
namespace与接口对应,id表示接口方法,resultType返回值POJO对象的类型
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jt.mapper.UserMapper">
<select id="findAll" resultType="com.jt.pojo.User">
select * from demo_user
select>
mapper>
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例
public class TestMybatis {
/**
* 规则说明:
* 1.创建SqlSessionFactory 工厂模式
* */
public void demo1() throws IOException {
//1.1指定配置文件的根目录
String resource = "mybatis-config.xml";
//1.2通过I/O流 加载配置文件 org.apache.ibatis.io.Resources包路径
InputStream inputStream = Resources.getResourceAsStream(resource);
//1.3实例化工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
}
既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
//2.1获取SqlSession 通过SqlSession可以直接操作数据库
SqlSession sqlSession = sqlSessionFactory.openSession();
//2.2获取接口对象
UserMapper usermapper = sqlSession.getMapper(UserMapper.class);
package com.jt;
import com.jt.mapper.UserMapper;
import com.jt.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.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class TestMybatis {
/**
* 规则说明:
* 1.创建SqlSessionFactory 工厂模式
* 2.获取SqlSession 数据库链接 + 传输器对象
* 3.获取Mapper接口对象
* 4.完成业务调用
* 5.关闭链接
* */
@Test
public void demo1() throws IOException {
//1.1指定配置文件的根目录
String resource = "mybatis-config.xml";
//1.2通过I/O流 加载配置文件 org.apache.ibatis.io.Resources包路径
InputStream inputStream = Resources.getResourceAsStream(resource);
//1.3实例化工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2获取SqlSession 通过SqlSession可以直接操作数据库
SqlSession sqlSession = sqlSessionFactory.openSession();
//3获取接口对象
UserMapper usermapper = sqlSession.getMapper(UserMapper.class);
//4.调用接口方法
List<User> userList = usermapper.findAll();
System.out.println(userList);
//5.暂时手动关闭链接
sqlSession.close();
}
}
数据库版本要求:MySQL5.5以上版本 mariaDB10 以上
驱动异常:
开箱即用规则
报错原理:由于Mybatis依赖了JDBC的jar包.,但是该jar包文件是启动项.,当主启动类运行时,开始加载主启动项。 但是JDBC需要链接数据库,所以必须有相关的配置信息.但是此时YML文件中没有数据源的配置,所以报错。
yml 文件 0 不解析 如果字母以0开题则引号包裹
配置application.yml文件:配置端口号、配置数据源、配置映射文件
#1.配置端口号 注意空格
server:
port: 8090
servlet:
context-path: /
#2.配置数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/jt?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
username: root
password: 123456
#3.配置映射文件
mybatis:
type-aliases-package: com.jt.pojo
#将所有的映射文件全部加载
mapper-locations: classpath:/mappers/*.xml
#开启驼峰映射
configuration:
map-underscore-to-camel-case: true
package com.jt.mapper;
import com.jt.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper //将接口交给Spring容器管理Map
public interface UserMapper {
//指定接口方法 查询demo_user所有数据
List<User> findAll();
}
package com.jt;
import com.jt.mapper.UserMapper;
import com.jt.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class MybatisDemo1ApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
public void testDemo1(){
List<User> userList = userMapper.findAll();
System.out.println(userList);
}
}
package com.jt.mapper;
import com.jt.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserMapper {
List<User> findAll();
//根据id查询数据库
User findUserById(int id);
}
@Test
public void testFindUserById(){
int id = 1;
userMapper.findUserById(id);
}
resultType 返回值的类型,封装为对象
<select id="findUserById" resultType="com.jt.pojo.User">
select * from demo_user where id = #{id}
select>
编辑yml文件
#4.打印sql com.jt.mapper 下的sql日志
logging:
level:
com.jt.mapper:debug
package com.jt.mapper;
import com.jt.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper //将接口交给Spring容器管理Map
public interface UserMapper {
//指定接口方法 查询demo_user所有数据
List<User> findAll();
//根据id查询数据库
User findUserById(int id);
List<User> findUserByNA(User user);
}
/**
* 知识点:
* 1.如果是多个参数传递,则一般采用对象的方式封装
* */
@Test
public void testFindByNa(){
String name = "孙尚香";
List<User> userList = userMapper.findUserByNA();
System.out.println(userList);
}
<select id="findUserByNA" resultType="com.jt.pojo.User">
select * from demo_user where name = #{name} and age = #{age}
select>
mapper>
如果多个参数不方便使用User对象封装时,应该使用万能的集合Map
如果传递的参数是Map,则使用#{key}
xml转义字符:
1. > >
2. < <
3. & &
万能转义:
<select id="findUserByAge" resultType="User">
select * from demo_user where age > #{minAge} and age > #{maxAge}
select>
/**
* 知识点:
* 如果多个参数不方便使用User对象封装时,应该使用万能的集合Map
*
* */
@Test
public void testFindUserByAge(){
int minAge = 18;
int maxAge = 100;
Map<String,Integer> map = new HashMap();
map.put("minAge", minAge);
map.put("maxAge", maxAge);
List<User> userList = userMapper.findUserByAge(map);
System.out.println(userList);
}
package com.jt.mapper;
import com.jt.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Map;
@Mapper //将接口交给Spring容器管理Map
public interface UserMapper {
//指定接口方法 查询demo_user所有数据
List<User> findAll();
//根据id查询数据库
User findUserById(int id);
List<User> findUserByNA(User user);
List<User> findUserByAge(Map<String, Integer> map);
}
mybatis只支持单值传参 将多值封装为单值
@Param将数据封装为Map集合
@Param(“key”) int minAge(值)
/**
* 知识点:
* 利用注解实现数据的封装,封装Map集合
* */
@Test
public void testFindUserByAge2(){
int minAge = 18;
int maxAge = 100;
List<User> userList = userMapper.findUserByAge2(minAge,maxAge);
System.out.println(userList);
}
//mybatis只支持单值传参 将多值封装为单值
//注解:@Param
List<User> findUserByAge2(@Param("minAge") int minAge, @Param("maxAge") int maxAge);
<select id="findUserByAge2" resultType="User">
#{minAge} and age < #{maxAge}
]]>
select>
查询name字段中包含“君”的用户
@Test
public void testFindUserByLike(){
//String name = "%"+"君"+"%";
String name = "君";
List<User> userList = userMapper.findUserByLike(name);
System.out.println(userList);
}
List<User> findUserByLike(String name);
<!--windows系统:数据库不区分大小写
linux系统: 区分大小写
-->
<select id="findUserByLike" resultType="User">
select * from demo_user where name like "%"#{name}"%"
</select>
说明:Sql语句中经常出现重复的数据,如果每次重复的内容都自己手写,开发效率低
用法:Sql标签
优化:将公共的Sql进行抽取
优势:Sql标签可以
<sql id="tableColumn">
id,name,age,sex
sql>
<select id="findUserByLike" resultType="User">
select <include refid="tableColumn"/> from demo_user where name like "%"#{name}"%"
select>
查询id=1,2,3,5,7的数据
@Test
public void testFindListByIn(){
int[] array = {1,2,3,5,7};
List<User> userList = userMapper.findListByIn(array);
System.out.println(userList);
}
List<User> findListByIn(int[] array);
关于Mybatis的遍历的写法:
foreach标签:
1.collection 需要遍历的集合
1.1数组 关键字:array
1.2list集合 关键字:list
1.3Map
2.open/close 循环体的开始和结束 可以写到循环之外简化标签
3.item 当前遍历数据的变量 是id
4.separator 分割符
<select id="findListByIn" resultType="User">
select * from demo_user where id in(
<foreach collection="array" item="id" separator=",">
#{id}
foreach>
)
select>
需求:
根据对象中不为null的数据,充当where条件,进行查询
编辑测试方法:
@Test
public void testDemo1(){
User user = new User();
user.setAge(18).setSex("女"); //动态变化的数据
List<User> userList = userMapper2.findUserList(user);
System.out.println(userList);
}
编辑接口类:
List<User> findUserList(User user);
编辑映射文件:
根据对象中不为空的属性当作where条件
语法:
1.如果判断成立则动态拼接属性 id = #{id}
2.where标签 去除where后边多余的and/or
<select id="findUserList" resultType="User">
select * from demo_user
<where>
<if test="id != null">id=#{id}if>
<if test="name != null">and name=#{name}if>
<if test="age != null"> and age=#{age}if>
<if test="sex != null"> and sex=#{sex}if>
where>
select>
编辑测试方法:
//执行动态的更新操作
//根据对中不为null的元素,充当set条件
@Test
public void testDemo2(){
User user = new User();
user.setId(235).setName("王二麻子").setAge(25).setSex("男");
userMapper2.updateUser(user);
System.out.println("更新成功");
}
编辑接口类:
void updateUser(User user);
编辑映射文件:
<update id="updateUser">
update demo_user
<set>
<if test="name != null">name = #{name},if>
<if test="age != null">age = #{age},if>
<if test="sex != null">sex = #{sex}if>
set>
where id = #{id}
update>
如果不想使用所有的条件可以使用choose类似于java中的switch
语法:如果name有值,则按照name查询,否则按照age查询,age无值则按照 sex查询数据
编辑测试方法:
@Test
public void testDemo3(){
User user = new User();
user.setName(null).setAge(18).setSex("女");
List<User> userList = userMapper2.findUserByNS(user);
System.out.println(userList);
}
编辑接口类:
List<User> findUserByNS(User user);
编辑映射文件:
<select id="findUserByNS" resultType="User">
select * from demo_user
<where>
<choose>
<when test="name != null">name =#{name}when>
<when test="age != null">age =#{age}when>
<otherwise>sex = #{sex}otherwise>
choose>
where>
select>
说明:当表中字段与pojo中的属性名称不一致时
,或者进行多表联查时
,需要使用resultMap的方式进行映射
表中字段名称与对象中属性名称不一致,结果数据不能映射
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class Dog {
private Integer dogId;
private String dogName;
private Integer age;
}
字段的名称
与属性的名称
一致时,才能自动映射
说明:在业务中经常出现该现象: 字段user_id 属性userId 属性和字段有驼峰映射规则,但是不采用resultType方式映射,则不能正常赋值。
解决方案:
1.resultMap 繁琐
2开启驼峰映射规则
1.一对一 一个员工对应一个部门
2.一个多 老师对应多个学生/一个部门对应多个员工
3.多对多 一个老师对应多个学生 一个学生对应多个老师(双向的多对多)
特点:只获取两张表的交集
SELECT * FROM emp,dept WHERE emp.dept_id=dept.dept_id
分类:1左连接(查询左表所有数据) 2内连接 3右连接(查询右表所有数据)
#左连接
SELECT * FROM emp LEFT JOIN dept ON emp.dept_id=dept.dept_id
员工对部门
1.如果操作单表 一般使用resultType
2.如果进行关联操作 一般使用resultMap
3.如果是多表关联操作,不允许出现重复字段名
**关于自动映射规则:**
1.没有映射关联时如果属性和字段名相同,则可以省略不写
2.有关联映射,则需要添加自动映射开关autoMapping="true"
,自动开关只对当前对象有效
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class Emp implements Serializable {
private Integer id;
private String name;
private Integer age;
//关联关系:一个员工对应一个部门
private Dept dept;
//private Integer deptId;
}
@Test
public void testOneToOne(){
List<Emp> empList = empMapper.findAll();
System.out.println(empList);
}
关联另外的表用标签,
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jt.mapper.EmpMapper">
<select id="findAll" resultMap="empRM">
select <include refid="tableColumn"/> from emp,dept where emp.dept_id=dept.dept_id
select>
<resultMap id="empRM" type="Emp" autoMapping="true">
<id column="id" property="id"/>
<association property="dept" javaType="Dept" autoMapping="true">
<id column="dept_id" property="deptId">id>
association>
resultMap>
<sql id="tableColumn">
emp.id,emp.name,emp.age,dept.dept_id,dept.dept_name
sql>
mapper>
部门对员工
//一对多,一个部门中要求获取多个员工
@Test
public void testOneToMore(){
List<Dept> deptList = deptMapper.findAll();
System.out.println(deptList);
}
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class Dept implements Serializable {
private Integer deptId;
private String deptName;
//关联关系:一个部门对应多个员工
private List<Emp> emps;
}
一对多关联另外表时用标签,
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jt.mapper.DeptMapper">
<select id="findAll" resultMap="deptRM">
select dept.dept_name,emp.*
from dept,emp
where dept.dept_id = emp.dept_id
select>
<resultMap id="deptRM" type="Dept" autoMapping="true">
<id column="dept_id" property="deptId"/>
<collection property="emps" ofType="Emp" autoMapping="true">
<id column="id" property="id"/>
collection>
resultMap>
mapper>
package com.jt;
import com.jt.mapper.DeptMapper;
import com.jt.mapper.EmpMapper;
import com.jt.mapper.UserAnnoMapper;
import com.jt.mapper.UserMapper;
import com.jt.pojo.Dept;
import com.jt.pojo.Emp;
import com.jt.pojo.User;
import org.apache.ibatis.annotations.Select;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@SpringBootTest
class MybatisDemo1ApplicationTests4 {
@Autowired
private UserAnnoMapper annoMapper;
@Test
//利用注解可以根据返回值类型,自动映射
public void testFindAll(){
List<User> list = annoMapper.findAll();
System.out.println(list);
}
@Test
//利用注解可以根据返回值类型,自动映射
public void testFindAll2(){
int id = 1;
List<User> list = annoMapper.findUserById(id);
System.out.println(list);
}
//根据姓名查询
@Test
public void testFindByNa(){
User user = new User();
user.setName("王六子");
List<User> list = annoMapper.findUserByNa(user);
System.out.println(list);
}
//插入数据
@Test
public void testInsert(){
User user = new User();
user.setName("刘阳").setAge(22).setSex("女");
int rows = annoMapper.insertUser(user);
System.out.println(rows);
}
//修改数据
@Test
public void testSetUser(){
User user = new User();
user.setId(236).setName("刘阳").setAge(21).setSex("女");
int rows = annoMapper.updateUser(user);
System.out.println(rows);
}
//删除数据
@Test
public void testDelete(){
User user = new User();
user.setId(236);
int rows = annoMapper.deleteUser(user);
System.out.println(rows);
}
}
package com.jt.mapper;
import com.jt.pojo.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
//该Mapper主要测试注解开发
@Mapper
@CacheNamespace //使用注解的二级缓存
public interface UserAnnoMapper {
@Select("select * from demo_user")
List<User> findAll();
@Select("select * from demo_user where id = #{id}")
List<User> findUserById(int id);
@Insert("insert into demo_user values(null,#{name},#{age},#{sex})")
int insertUser(User user);
@Update("update demo_user set id= #{id},age = #{age},sex = #{sex} where name = #{name}")
int updateUser(User user);
@Delete("delete from demo_user where id = #{id}")
int deleteUser(User user);
@Select("select * from demo_user where name = #{name}")
List<User> findUserByNa(User user);
List<User> findCache1();
}
说明: 如果相同的数据需要多次查询,则可以使用缓存的方式处理,提高用户的响应速度。
mybatis中提供了两种缓存机制:
一级缓存: SqlSession级别 在同一个sqlSession内实现数据共享 默认开启
二级缓存:SqlSessionFactory级别 由同一个sqlSessionFactory生产的SqlSession数据共享 默认开启 + 配置
易错项:二级缓存默认开启
说明:SpringBoot在使用一二级缓存时,有个别特殊要求注意
现象:采用SpringBoot的方式进行测试时,sql执行多次,一级缓存无效
解决:添加事务的注解,SpringBoot中添加了之后,默认采用一个SqlSession
编辑测试方法:
@Test
@Transactional //添加事务
public void testCache1(){
List<User> userList1 = userMapper.findCache1();
List<User> userList2 = userMapper.findCache1();
List<User> userList3 = userMapper.findCache1();
List<User> userList4 = userMapper.findCache1();
}
测试策略:
说明:为了构建多个mapper对象,需要准备多个线程进行测试,可以通过浏览器让用户发起多次请求之后,测试二级缓存是否有效。
层级代码结构:
List<User> findCache1();
编辑service层:
编辑UserService接口类:
package com.jt.service;
import com.jt.pojo.User;
import java.util.List;
public interface UserService {
List<User> findCache1();
List<User> findAll();
}
编辑UserServiceImpl实现类:
package com.jt.service;
import com.jt.mapper.UserAnnoMapper;
import com.jt.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserAnnoMapper userMapper;
@Override
public List<User> findCache1() {
return userMapper.findCache1();
}
@Override
public List<User> findAll() {
return userMapper.findAll();
}
}
编辑controller类:
package com.jt.controller;
import com.jt.pojo.User;
import com.jt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@ResponseBody
public class UserController {
@Autowired
private UserService userService;
/**
* 要求返回List集合的JSON串
* */
@GetMapping("/findCache")
public List<User> findCache1(){
return userService.findCache1();
}
@GetMapping("/findAll")
public List<User> findAll(){
return userService.findAll();
}
}
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jt.mapper.UserAnnoMapper">
<cache/>
<select id="findCache1" resultType="User">
select * from demo_user
select>
mapper>
业务说明: Mybatis中采用注解方式的查询和xml映射文件的查询的方式不一样. 两者不可以混用.
使用注解的二级缓存。
parameterType为传入参数类型,resultType传出参数类型,resultMap当实体类属性名与数据库字段名不相同时使用