![](https://i-blog.csdnimg.cn/blog_migrate/69765f2c2ac148c2b9a4a4c34a83a312.png)
Mybatis介绍
MyBatis本是Apache软件基金会的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了Google Code,并且改名为MyBatis 。2013年11月迁移到Github。
MyBatis是一个优秀的基于Java的持久层框架,支持自定义SQL,存储过程和高级映射。
MyBatis对原有JDBC操作进行了封装,几乎消除了所有JDBC代码,使开发者只需关注 SQL 本身。
MyBatis可以使用简单的XML或Annotation来配置执行SQL,并自动完成ORM操作,将执行结果返回。
Mybatis框架介绍
2.1 什么是框架?
软件的半成品,解决了软件开发过程当中的普适性问题,从而简化了开发步骤,提供了开发的效率。
2.2什么是ORM框架?
ORM(Object Relational Mapping)对象关系映射,将程序中的一个对象与表中的一行数据一一对应。
ORM框架提供了持久化类与表的映射关系,在运行时参照映射文件的信息,把对象持久化到数据库中
2.3 使用JDBC完成ORM操作的缺点?
存在大量的冗余代码。
手工创建 Connection、Statement 等。
手工将结果集封装成实体对象。
查询效率低,没有对数据访问进行过优化(Not Cache)。
2.4访问与下载
官方网站: http://www.mybatis.org/mybatis-3/
下载地址: https://github.com/mybatis/mybatis-3/releases/tag/mybatis-3.5.1
Mybatis入门—CRUD
查询全部
模糊查询三种写法
根据id查询一条数剧
添加,返回主键id
批量删除
修改
分页
3.1建表
CREATE TABLE t_user(
id INT NOT NULL AUTO_INCREMENT COMMENT 'id 编号' ,
username VARCHAR(128) NOT NULL COMMENT 'name 姓名' ,
password VARCHAR(128) NOT NULL COMMENT 'password 密码' ,
PRIMARY KEY (id)
) COMMENT = '用户表';
INSERT INTO t_user(USERNAME,PASSWORD) VALUES("jack","123");
INSERT INTO t_user(USERNAME,PASSWORD) VALUES("tom","456");
INSERT INTO t_user(USERNAME,PASSWORD) VALUES("rose","789");
3.2pom.xml中引入MyBatis核心依赖
在pom.xml中引入相关依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>创项目自己设置</groupId>
<artifactId>mybatis-01</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- 导入mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
</project>
3.3 创建log4j.properties配置文件
在resources目录下创建log4j.properties
log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
级别 | 描述 |
ALL LEVEL | 打开所有日志记录开关;是最低等级的,用于打开所有日志记录。 |
INFO | 输出提示信息;消息在粗粒度级别上突出强调应用程序的运行过程。 |
WARN | 输出警告信息;表明会出现潜在错误的情形。 |
ERROR | 输出错误信息;指出虽然发生错误事件,但仍然不影响系统的继续运行。 |
FATAL | 输出致命错误;指出每个严重的错误事件将会导致应用程序的退出。 |
OFF LEVEL | 关闭所有日志记录开关;是最高等级的,用于关闭所有日志记录。 |
3.4创建mybatis-config.xml配置文件
在resources目录下创建并配置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">
<!-- mybatis的主配置文件 -->
<configuration>
<!-- 配置外部文件 -->
<properties resource="db.properties"></properties>
<!-- 配置日志(log4j官方配置) -->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<!-- 根据需要给实体类配置别名 -->
<typeAliases>
<typeAlias type="com.pojo.User" alias="user"></typeAlias>
</typeAliases>
<!-- 官网环境配置 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!-- 配置数据源(连接池) -->
<dataSource type="POOLED">
<property name="driver" value="${db.driverClassName}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
</configuration>
3.5定义实体类
定义所需CURD操作的实体类
package com.pojo;
import lombok.Data;
@Data
public class User{
private Integer id;
private String username;
private String password;
}
3.6定义接口
在接口中创建相关方法,接口命名不同公司会有不同要求,一般是:xxDao 或者 xxMapper
package com.dao;
import com.pojo.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface UserDao {
/**
* @parea selectUser查询user表下所有的数据
* @parea selectLike1模糊查询
* @parea selectLike2模糊查询
* @parea selectLike3模糊查询
* @parea selsecOne查询一条数据
* @return
*/
List<User> selectUser();
List<User> selectLike1(String username);
List<User> selectLike2(String username);
List<User> selectLike3(String username);
User selsecOne(int id);
/**
* 添加 查询所添加数据的id
* @param user
* @return
*/
int adds(User user);
/**
* 批量删除
* @param id
* @return
*/
int del(int[] id);
/**
* 修改
* @param user
* @return
*/
void update(User user);
/**
*打印数据库数据数量
* @return
*/
Integer getCount();
/**
*如果方法中有多个参数的话,必须在参数的前面使用@Param注解 名称 和sql语句中#{} 名称保持一致
* @param first
* @param lsat
* @return
*/
List<User> pagenum(@Param("first") Integer first,@Param("last") Integer lsat);
}
3.7编写Mapper.xml
在resources目录中下创建与接口对应的路径和对应名称的Mapper.xml文件
注意:如果是在resources目录下创建多个目录,创建时为:com/xxx/yyy(显示的时候为:com.xxx.yyy),切记!!!
<?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">
<!--写SQL语句-->
<!--namespace = 所需实现的接口全限定名-->
<mapper namespace="com.dao.UserDao">
<!-- 属性:id 对应接口下面的方法名
resultType-->
<!-- 起别名-->
<resultMap id="userMap" type="User">
<!-- <resultMap id="userMap" type="com.pojo.User">-->
<!-- 主键属性的声明 -->
<id property="id" column="id"></id>
<!-- 其他属性的声明 -->
<result property="username" column="username"></result>
<result property="password" column="password"></result>
</resultMap>
<!-- 查询全部-->
<select id="selectUser" resultType="User">
<!-- <select id="selectUser" resultType="com.pojo.User">-->
select id, username, password
from user
</select>
<!-- 模糊查询写法1-->
<select id="selectLike1" resultType="com.pojo.User">
select *
from user
where username like #{username}
</select>
<!-- 模糊查询写法2-->
<select id="selectLike2" resultType="com.pojo.User">
select *
from user
where username like concat('%', #{name}, '%')
</select>
<!-- 模糊查询3
#{} 和 ${}区别:
1.#{}是占位符,可以防止sql注入;${}是拼接符,不可以防止sql注入
2.能用#{}的地方尽量使用#{},在使用order by语句时,必须使用${}
3.只有一个基本数据类型的参数时,${}中的名称建议写value,不要写其它名称,会报错
(Mybatis3.4版本中存在这种现象,3.5版本修改后就没有了)
-->
<select id="selectLike3" resultType="com.pojo.User">
select *
from user
where username like "%${value}%"
</select>
<!--查询一条数据-->
<select id="selsecOne" resultType="com.pojo.User">
select *
from user
where id = #{id}
</select>
<!-- 添加一条数据 返回主键id-->
<insert id="adds">
<!--select last_insert_id()获取最后添加字段的id
keyProperty 对象的属性名
keyColumn字段的属性名
resultType返回的类型
order 排序的 执行sql之前还是之后返回 AFTER 之后-->
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id()
</selectKey>
insert into user(username, password) values(#{username}, #{password})
</insert>
<!-- 批量删除-->
<delete id="del">
delete from user
<where>
id in
<!--foreach 动态sql的标签 循环 遍历数组(array)集合(list) 可以批量处理 批量新增 批量删除
常用属性:
collection 对应的参数的类型 小写,固定 内置
item 元素变量
separator 多个值直接间隔符
open 开始的符号
close 关闭的符合
内部还是根据 #{元素变量} 拼接内容-->
<foreach collection="array" item="i" separator="," open="(" close=")">
#{i}
</foreach>
</where>
</delete>
<!-- 修改-->
<update id="update">
update user
set username=#{username},
password=#{password}
where id = #{id}
</update>
<!-- 获取数据库数据数量-->
<select id="getCount" resultType="int">
select count(*)
from user
</select>
<!-- 分页-->
<select id="pagenum" resultType="User">
select *
from user limit #{first},#{last}
</select>
</mapper>
3.8注册Mapper
<!--Mapper注册-->
<mappers>
<!--注册Mapper文件的所在位置-->
<!-- 指定映射配置文件的位置,映射配置文件指的是每个接口独立的配置文件 -->
<!--三种写法 选一即可-->
<!-- 加载普通的xml文件,传入xml的相对路径(相对于类路径)-->
<mapper resource="com/dao/UserMapper.xml"></mapper>
<!-- 使用mapper接口的全限定名来加载-->
<mapper class="com.dao.UserDao"/>
<!-- 扫描指定包下的所有mapper-->
<package name="com.dao"/>
</mappers>
注意:用后两种方式加载mapper接口和mapper.xml映射文件时,可能会报错
3.9测试
在\src\test\java目录下创建测试类进行测试
//这里简单用了一个工具类 其他功能可以自行添加
public class BatisUtil {
private static SqlSession session;
static {
try {
//读取配置文件
InputStream res = Resources.getResourceAsStream("mybatis-config.xml");
//创建SqlSessionFactoryBuilder 对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(res);
//用SqlSessionFactory创建sqlSession对象
//可以执行sql语句
session = factory.openSession(true);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取指定的dao对象*/
public static <T> T getMapper(Class<T> clz){
return session.getMapper(clz);
}
/**
* 提交事务*/
public static void commit(){
session.commit();
}
}
测试
/**
* 查询全部
*/
@Test
public void selectUser(){
UserDao userDao = BatisUtil.getMapper(UserDao.class);
System.out.println(userDao.selectUser());
List<User> users = userDao.selectUser();
for (User user : users) {
System.out.println(user);
}
}
/**
* 模糊查询
*/
@Test
public void selectLike(){
UserDao mapper = BatisUtil.getMapper(UserDao.class);
//List<User> users = mapper.selectLike1("迪迦");
//List<User> users = mapper.selectLike2("迪迦");
List<User> users = mapper.selectLike3("迪迦");
for (User user : users) {
System.out.println(user);
}
}
/**
* 查询一条数据
*/
@Test
public void selsecOne(){
System.out.println( BatisUtil.getMapper(UserDao.class).selsecOne(1));
}
/**
* 添加 主键返回
*/
@Test
public void adds(){
UserDao mapper = BatisUtil.getMapper(UserDao.class);
User user = new User();
user.setUsername("戴拿");
user.setPassword(666);
mapper.adds(user);
System.out.println("添加字段的id为"+ user.getId());
}
/**
* 批量删除
*/
@Test
public void del(){
UserDao userDao = BatisUtil.getMapper(UserDao.class);
//根据数据库字段自行添加
int[] objs={11,12,13};
System.out.println(userDao.del(objs));
}
/**
* 修改
*/
@Test
public void update(){
UserDao mapper = BatisUtil.getMapper(UserDao.class);
User user = new User();
user.setUsername("闪耀迪迦");
user.setPassword(999);
user.setId(21);
mapper.update(user);
}
/**
* 查询全部数据
*/
@Test
public void getCount(){
UserDao mapper = BatisUtil.getMapper(UserDao.class);
Integer count = mapper.getCount();
System.out.println(count);
}
分页测试
@Test
public void t1(){
UserDao mapper = BatisUtil.getMapper(UserDao.class);
/**
* @Param 第一个参数: 表示当前页 pageNum
* @Param 第二个参数: 每页显示的条数
*/
PageHelper.startPage(1,3);
List<User> users = mapper.selectUser();
//users所有的数据,将所有的数据封装到 分页插件中 让其所有数据实现分页
PageInfo<User> userPageInfo = new PageInfo<>(users);
//System.out.println(userPageInfo);
//System.out.println("分页的数据:" + userPageInfo.getList());
//打印该页所有数据
List<User> list = userPageInfo.getList();
for (User user : list) {
System.out.println(user);
}
/**
* @Param getPageNum 当前页
* @Param getPageSize 每页显示的条数
* @Param getTotal 总条数
* @Param getPages 总页数
*/
System.out.println("当前页:" + userPageInfo.getPageNum());
System.out.println("每页显示的条数:" + userPageInfo.getPageSize());
System.out.println("总条数:" + userPageInfo.getTotal());//long
System.out.println("总页数:" + userPageInfo.getPages());
}
MyBatis处理关联关系-多表连接
实体间的关系:关联关系
OneToOne:一对一关系(Passenger --- Passport):一个旅客只有一个护照,一个护照只对应一个旅客
OneToMany:一对多关系(Department --- Employee):一个部门有多个员工,一个员工只属于一个部门
ManyToMany:多对多关系(Student --- Subject):一个学生学习多门课程,一个课程被多个学生学习
4.1一对一(OneToOne)
建表
-- 旅客表
CREATE TABLE t_passenger(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
sex VARCHAR(1),
birthday DATE
)DEFAULT CHARSET =utf8;
INSERT INTO t_passenger VALUES(1,'zhansan','f','2020-11-11');
INSERT INTO t_passenger VALUES(2,'lucy','m','2020-12-12');
-- 护照表
CREATE TABLE t_passport(
id INT PRIMARY KEY AUTO_INCREMENT,
nationality VARCHAR(50),
expire DATE,
passenger_id INT UNIQUE
)DEFAULT CHARSET =utf8;
INSERT INTO t_passport VALUES(10001,'China','2030-11-11',1);
INSERT INTO t_passport VALUES(10002,'America','2030-12-12',2);
创建对应的实体类
package com.pojo;
import lombok.Data;
import java.util.Date;
/**
* 旅客
*/
@Data
public class Passenger {
private Integer id;
private String name;
private String sex;
private Date birthDay;
//用于映射查询对应的相关信息,一的一方
private Passport passport;
}
package com.pojo;
import lombok.Data;
import java.util.Date;
/**
* 护照
*/
@Data
public class Passport {
private Integer id;
private String nationality;
private Date expire;
private Integer passengerId;
}
创建接口
package com.dao;
import com.pojo.Passenger;
public interface PassengerDao {
/**
* 根据id查询旅客
* @param id
* @return
*/
public Passenger findByPassengerId(Integer id);
}
创建PassengerMapper.xml配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dao.PassengerDao">
<!-- 声明resultMap -->
<resultMap id="userMap" type="com.pojo.Passenger">
<!-- 主键属性的声明 -->
<id property="id" column="id"></id>
<!-- 其他属性的声明 -->
<result property="name" column="name"></result>
<result property="sex" column="sex"></result>
<result property="birthDay" column="birthDay"></result>
<!-- 一的一方映射 -->
<association property="passport" javaType="com.pojo.Passport">
<!-- 主键属性的声明 -->
<id property="id" column="passportId"></id>
<!-- 其他属性的声明 -->
<result property="nationality" column="nationality"></result>
<result property="expire" column="expire"></result>
</association>
</resultMap>
<!-- 查询所有用户,如果属性名和数据库中的列名不一致,我们可以使用resultMap标签 -->
<select id="findByPassengerId" resultMap="userMap">
SELECT p1.id,p1.name,p1.sex,p1.birthday,p2.id AS passportId,p2.nationality,p2.expire
FROM t_passenger p1
INNER JOIN t_passport p2
ON p1.id = p2.passenger_id
WHERE p1.id = #{id}
</select>
</mapper>
测试
@Test
public void findByPassengerId(){
PassengerDao mapper = BatisUtil.getMapper(PassengerDao.class);
Passenger byPassengerId = mapper.findByPassengerId(1);
System.out.println(byPassengerId);
}
4.2一对多(OneToMany)
创建表
CREATE TABLE t_department(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(50),
location VARCHAR(100)
)DEFAULT CHARSET =utf8;
INSERT INTO t_department VALUES(1,"教学部","北京"),(2,"研发部","上海");
CREATE TABLE t_employee(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(50),
salary DOUBLE,
dept_id INT
)DEFAULT CHARSET =utf8;
INSERT INTO t_employee VALUES(1,"jack",1000.5,1);
INSERT INTO t_employee VALUES(2,"rose",2000.5,1);
INSERT INTO t_employee VALUES(3,"张三",3000.5,2);
INSERT INTO t_employee VALUES(4,"李四",4000.5,2);
创建实体类
/**
* 部门
*/
@Data
public class Department {
private Integer id;
private String name;
private String location;
}
/**
* 员工
*/
@Data
public class Employee {
private Integer id;
private String name;
private Double salary;
//一的一方,写的是对象属性
private Department department;
创建接口
public interface DepartmentDao {
/**
* 根据id查询部门
* @param id
* @return
*/
public Department selectByIdDepartment(Integer id);
}
创建DepartmentMapper.xml配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dao.DepartmentDao">
<!-- 声明resultMap -->
<resultMap id="userMap" type="com.pojo.Department">
<!-- 主键属性的声明 -->
<id property="id" column="id"></id>
<!-- 其他属性的声明 -->
<result property="name" column="name"></result>
<result property="location" column="location"></result>
<!-- 一的一方映射 -->
<collection property="employeeList" ofType="com.pojo.Employee">
<!-- 主键属性的声明 -->
<id property="id" column="empId"></id>
<!-- 其他属性的声明 -->
<result property="name" column="empName"></result>
<result property="salary" column="salary"></result>
</collection>
</resultMap>
<!-- 查询所有部门,如果属性名和数据库中的列名不一致,我们可以使用resultMap标签 -->
<select id="selectByIdDepartment" resultMap="userMap">
SELECT d.id,d.name,d.location,e.id AS empId,e.name AS empName,e.salary
FROM t_department d INNER JOIN t_employee e ON d.id = e.dept_id
WHERE d.id = #{id}
</select>
</mapper>
测试
@Test
public void selectByIdDepartment(){
DepartmentDao mapper = BatisUtil.getMapper(DepartmentDao.class);
Department department = mapper.selectByIdDepartment(1);
System.out.println(department);
}
4.3多对多(ManyToMany)
![](https://i-blog.csdnimg.cn/blog_migrate/3dfddecdb151886686f1f143fba9155d.png)
建表
CREATE TABLE t_student(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(50),
sex VARCHAR(1)
)DEFAULT CHARSET = utf8;
INSERT INTO t_student VALUES(1,'jack','m');
INSERT INTO t_student VALUES(2,'rose','f');
CREATE TABLE t_subject(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(50),
grade INT
)DEFAULT CHARSET = utf8;
INSERT INTO t_subject VALUES(1001,'JavaSE',1);
INSERT INTO t_subject VALUES(1002,'JavaEE',2);
CREATE TABLE t_stu_sub(
student_id INT,
subject_id INT
)DEFAULT CHARSET = utf8;
INSERT INTO t_stu_sub VALUES(1,1001);
INSERT INTO t_stu_sub VALUES(1,1002);
INSERT INTO t_stu_sub VALUES(2,1001);
INSERT INTO t_stu_sub VALUES(2,1002);
实体类
/**
* 学生
*/
@Data
public class Student {
private Integer id;
private String name;
private String sex;
//多对多,都是多的一方
private List<Subject> subjectList;
}
/**
* 课程
*/
@Data
public class Subject {
private Integer id;
private String name;
private Integer grade;
}
创建接口
public interface StudentDao {
/**
* 通过id查询学生以及对应的课程
* @param id
* @return
*/
public Student findByStudentId(Integer id);
}
创建StudentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dao.StudentDao">
<!-- 声明resultMap -->
<resultMap id="userMap" type="com.pojo.Student">
<!-- 主键属性的声明 -->
<id property="id" column="id"></id>
<!-- 其他属性的声明 -->
<result property="name" column="name"></result>
<result property="sex" column="sex"></result>
<!-- 一的一方映射 -->
<collection property="subjectList" ofType="com.pojo.Subject">
<!-- 主键属性的声明 -->
<id property="id" column="subjectId"></id>
<!-- 其他属性的声明 -->
<result property="name" column="subjuectName"></result>
<result property="grade" column="grade"></result>
</collection>
</resultMap>
<!-- 查询所有用户,如果属性名和数据库中的列名不一致,我们可以使用resultMap标签 -->
<select id="findByStudentId" resultMap="userMap">
SELECT sy.id,sy.Name,sy.sex,su.id as subjectId,su.NAME as subjuectName,su.grade
FROM t_student sy
INNER JOIN t_stu_sub
on sy.id=t_stu_sub.student_id
INNER JOIN t_subject su
on t_stu_sub.subject_id=su.id
where sy.id = #{id}
</select>
</mapper>
测试
@Test
public void findByStudentId(){
StudentDao mapper = BatisUtil.getMapper(StudentDao.class);
Student student = mapper.findByStudentId(1);
System.out.println(student);
}
4.4 关系总结
双方均可建立关系属性,建立关系属性后,在对应的Mapper文件中使用< ResultMap >完成多表映射。
如果对应的对象是一的一方,使用< association property="" javaType="" >
如果对应的对象是多的一方,使用< collection property="" ofType="" >
动态SQL
MyBatis的映射文件中支持在基础SQL上添加一些逻辑操作,并动态拼接成完整的SQL之后再执行,以达到SQL复用、简化编程的效果
5.1准备
创建表
CREATE TABLE `t_car` (
`id` INT(11) PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(256),
`brand` VARCHAR(256),
`price` DOUBLE,
`color` VARCHAR(256),
`num` INT(11)
) ENGINE = INNODB CHARACTER SET = utf8;
INSERT INTO t_car(NAME,brand,price,color,num) VALUES('迈腾','大众',200000,'黑色',5000);
INSERT INTO t_car(NAME,brand,price,color,num) VALUES('帕萨特','大众',200000,'黑色',6000);
INSERT INTO t_car(NAME,brand,price,color,num) VALUES('卡宴','保时捷',700000,'白色',2000);
INSERT INTO t_car(NAME,brand,price,color,num) VALUES('奥迪Q3','奥迪',300000,'白色',3000);
INSERT INTO t_car(NAME,brand,price,color,num) VALUES('宝马X5','宝马',500000,'黑色',2000);
INSERT INTO t_car(NAME,brand,price,color,num) VALUES('雅阁','本田',180000,'黑色',3000);
实体类
/**
* 如果对象涉及到IO流传以及缓存操作,必须实现序列化接口
* public class Car implements Serializable
*/
@Data
public class Car{
private Integer id;
private String name;
private String brand;
private Double price;
private String color;
private Integer num;
}
//创建CarDao
public interface CarDao {
/**
* 查询全部
* @return
*/
public List<Car> findAll();
/**
* 根据id查询一个
* @param id
* @return
*/
public Car findAllById(Integer id);
/**
* 模糊查询
* @param carVo
* @return
*/
public List<Car> findCar(CarVo carVo);
/**
* 修改
* @param car
*/
public void update(Car car);
/**
* 批量删除
* @param id
* @return
*/
public int delect(int[] id);
}
<!--创建CarMapper.xml,编写相关代码测试动态SQL标签-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dao.CarDao">
<!-- 声明resultMap -->
<resultMap id="CarMap" type="com.pojo.Car">
<!-- 主键属性的声明 -->
<id property="id" column="id"></id>
<!-- 其他属性的声明 -->
<result property="name" column="name"></result>
<result property="brand" column="brand"></result>
<result property="price" column="price"></result>
<result property="color" column="color"></result>
<result property="num" column="num"></result>
</resultMap>
</mapper>
5.2<sql>
在CarMapper.xml中添加对应查询所有和查询单个的方法
<sql id="baseSql">
select id,name,brand,price,color,num from t_car
</sql>
<!-- 查询所有用户,如果属性名和数据库中的列名不一致,我们可以使用resultMap标签 -->
<select id="findAll" resultMap="CarMap">
<!-- 引入sql片段 -->
<include refid="baseSql"></include>
</select>
<!-- 根据id查询一个用户-->
<select id="findAllById" resultMap="CarMap">
<!-- 引入sql片段 -->
<include refid="baseSql"></include>
where id = #{id}
</select>
5.3< if > 和 < where >
使用条件查询进行测试
<select id="findCar" resultMap="CarMap">
<include refid="baseSql"></include>
<where>
<if test="CarName != null and CarName !=''">
name=#{CarName}
</if>
<if test="CarBrand != null and CarBrand !=''">
and brand=#{CarBrand}
</if>
<if test="CarColor != null and CarColor !=''">
and color=#{CarColor}
</if>
<if test="CarPrice != null and CarPrice !=''">
and price=#{CarPrice}
</if>
</where>
</select>
5.4< set >和< trim >
set:可以忽略判断中间隔的都逗号
trim:< trim prefix="" suffix="" prefixOverrides="" suffixOverrides="" >代替< where > 、< set >同样是修改,写法不同而已
<update id="update">
update t_car
<!-- <set>-->
<!-- set标签可以忽略逗号 -->
<!-- <if test="name !=null and name!=''">-->
<!-- name=#{name},-->
<!-- </if>-->
<!-- <if test="color !=null and color!=''">-->
<!-- color=#{color}-->
<!-- </if>-->
<!-- <where>-->
<!-- <if test="id!=null">-->
<!-- id = #{id}-->
<!-- </if>-->
<!-- </where>-->
<!-- </set>-->
<trim prefix="set" prefixOverrides=",">
<if test="name !=null and name !='' ">
name = #{name},
</if>
<if test="color !=null and color !='' ">
color = #{color}
</if>
</trim>
<trim prefix="where" prefixOverrides="and|or">
<if test="id != null">
id = #{id}
</if>
</trim>
</update>
5.5< foreach >
<!--foreach 动态sql的标签 循环 遍历数组(array)集合(list) 可以批量处理 批量新增 批量删除
常用属性:
collection 对应的参数的类型 小写,固定 内置
如果参数是数组,则值为:array
如果参数是List集合,则值为:list
如果参数是set或者其他集合,则值为:@Param("值")
item 元素变量
separator 多个值直接间隔符
open 开始的符号
close 关闭的符合
内部还是根据 #{元素变量} 拼接内容-->
<delete id="delect">
delete from t_car
<where>
id in
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</where>
</delete>
注解写法(了解)
MyBatis除了使用xml方式操作数据库,也可以通过在接口中直接添加MyBatis注解,进行操作
将SQL语句写到代码文件中,后续的维护性和扩展性不是很好(如果想修改SQL语句,就得改代码,得重新打包部署,而如果用xml方式,则只需要修改xml,用新的xml取替换旧的xml)
另外mapper.xml就可以省掉了 可以直接在dao层写sql语句,然后在全局配置文件中直接指定加载这个类
@Repository//一般用在持久层
public interface UserDao {
/**
* 查询所有用户
* @return
*/
@Select("select id,username,password from user")
List<User> selectAllUser();
}
缓存
内存中的一块存储空间,服务于某个应用程序,旨在将频繁读取的数据临时保存在内存中,便于二次快速访问。
![](https://i-blog.csdnimg.cn/blog_migrate/8762af33eac8fb4a740e6c1a5ed01f1e.png)
7.1一级缓存
一级缓存是SqlSession级别的缓存,同一个SqlSession的发起多次同构查询,会将数据保存在一级缓存中。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
注意:无需任何配置,默认开启一级缓存
![](https://i-blog.csdnimg.cn/blog_migrate/a757c9cbd7449e71215733f3932998fc.png)
在CarDao中添加相关方法
//查询单个
public Car findAllById(Integer id);
在CarMapper.xml中添加相关代码
<!-- 查询单个 -->
<select id="findAllById" resultMap="carMap">
<!-- 引入sql片段 -->
<include refid="baseSql"></include>
<where>
id = #{id}
</where>
</select>
测试类中进行测试
@Test
public void findAllById() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//使用同一个sqlSession进行操作
SqlSession sqlSession = sessionFactory.openSession();
CarDao mapper = sqlSession.getMapper(CarDao.class);
Car one = mapper.selectAllByOne(1);
//手动清楚一级缓存
//sqlSession.clearCache();
Car ome1 = mapper.selectAllByOne(1);
//验证一级缓存是否存在
System.out.println(one == ome1);
//关闭
sqlSession.close();
inputStream.close();
}
7.2二级缓存
SqlSessionFactory级别的缓存,同一个SqlSessionFactory构建的不同SqlSession发起的多次同构查询,会将数据保存在二级缓存中
注:在sqlSession.commit()或者sqlSession.close()之后生效。
开启全局缓存
< settings >是MyBatis中极为重要的调整设置,他们会改变MyBatis的运行行为,其他详细配置可参考官方文档。
<configuration>
<properties .../>
<!-- 注意书写位置 -->
<settings>
<!--true为开启 -->
<setting name="cacheEnabled" value="false"/> <!-- mybaits-config.xml中开启全局缓存(默认开启) -->
</settings>
<typeAliases></typeAliases>
</configuration>
指定Mapper缓存
在CarMapper.xml中添加相关代码
<!-- 开启当前二级缓存 -->
<cache/>
测试
/**
* 二级缓存
*/
@Test
public void findAllById2() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//使用同一个sqlSession进行操作
SqlSession sqlSession1 = sessionFactory.openSession();
CarDao mapper1 = sqlSession1.getMapper(CarDao.class);
Car one1 = mapper1.selectAllByOne(1);
sqlSession1.close();
SqlSession sqlSession2 = sessionFactory.openSession();
CarDao mapper2 = sqlSession2.getMapper(CarDao.class);
Car one2 = mapper2.selectAllByOne(1);
}
![](https://i-blog.csdnimg.cn/blog_migrate/167f86b558ea36d607e8773f7046686c.png)
8.嵌套查询和延迟加载
相关准备:这里用之前的部门表
相关的实体类上不能使用@Data,@ToString等注解,否则会导致延迟加载失效
//实体类
/**
* 员工
* 实现延迟加载不能使用@Data,@ToString 注解
*/
//@Data
public class Employee {
private Integer id;
private String name;
private Double salary;
//一的一方,写的是对象属性
private Department department;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", salary=" + salary +
", department=" + department +
'}';}
}
编写EmployeeDao
public interface EmployeeDao {
/**
* 通过id查询员工
* @return
*/
public Employee selectAllById(Integer id);
}
配置EmployeeMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dao.EmployeeDao">
<!-- 声明resultMap -->
<resultMap id="userMap" type="com.pojo.Employee">
<!-- 主键属性的声明 -->
<id property="id" column="empId"></id>
<!-- 其他属性的声明 -->
<result property="name" column="empName"></result>
<result property="salary" column="salary"></result>
<!-- 嵌套查询
fetchType="lazy" 表示懒加载,延迟加载
fetchType="eager" 表示立即加载
-->
<association property="department" javaType="com.pojo.Department"
select="com.dao.DepartmentDao.selectAllById" column="dept_id"
fetchType="lazy">
</association>
</resultMap>
<!-- 只查询单个员工 -->
<select id="selectAllById" resultMap="userMap">
SELECT e.id AS empId,e.name AS empName,e.salary,e.dept_id
FROM t_employee e
WHERE e.id = #{id}
</select>
</mapper>
配置DepartmentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dao.DepartmentDao">
<!-- 声明resultMap -->
<resultMap id="userMap" type="com.pojo.Department">
<!-- 主键属性的声明 -->
<id property="id" column="id"></id>
<!-- 其他属性的声明 -->
<result property="name" column="name"></result>
<result property="location" column="location"></result>
<!-- 一的一方映射 -->
<collection property="employeeList" ofType="com.pojo.Employee">
<!-- 主键属性的声明 -->
<id property="id" column="empId"></id>
<!-- 其他属性的声明 -->
<result property="name" column="empName"></result>
<result property="salary" column="salary"></result>
</collection>
</resultMap>
<!-- 查询所有部门,如果属性名和数据库中的列名不一致,我们可以使用resultMap标签 -->
<select id="selectByIdDepartment" resultMap="userMap">
SELECT d.id,d.name,d.location,e.id AS empId,e.name AS empName,e.salary
FROM t_department d INNER JOIN t_employee e ON d.id = e.dept_id
WHERE d.id = #{id}
</select>
<!-- 只查询单个部门 -->
<select id="selectAllById" resultMap="userMap">
SELECT d.id,d.name,d.location
FROM t_department d
WHERE d.id = #{id}
</select>
</mapper>
配置延迟加载
好处:开启延迟加载后,如果不使用及联数据,则不会触发及联查询操作,有利于加快查询速度、节省内存资源。
mybatis-config.xml中开启延迟加载配置
<settings>
<!-- 开启延迟加载,懒加载,默认false -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 将积极加载改为消息加载即按需加载 -->
<setting name="aggressiveLazyLoading" value="false" />
<!-- 由于重写了实体类中toString方法,需要排除不相干的方法导致的立即加载情况-->
<setting name="lazyLoadTriggerMethods" value="toString()"/>
</settings>
测试
/**
* 根据部门id查询员工
*/
@Test
public void selectAllById(){
EmployeeDao mapper = BatisUtil.getMapper(EmployeeDao.class);
Employee employee = mapper.selectAllById(1);
System.out.println(employee.getDepartment().getName());
}
Mybatis其他相关
$符号
${attribute} 属于字符串拼接SQL,而非预编译占位符,会有注入攻击问题,不建议在常规SQL中使用,常用于可解决动态生降序问题。
public List<User> selectAllUsers1(User user); // ${name} ${id} 可获取user中的属性值
public List<User> selectAllUsers2(@Param("rule") String rule); //必须使用@Param否则会作为属性解析
<select id="selectAllUsers1" resultType="user">
SELECT * FROM t_users
WHERE name = '${name}' or id = ${id} <!-- 拼接name和id,如果是字符类型需要用单引号:'${name}' -->
</select>
<select id="selectAllUsers2" resultType="user">
SELECT * FROM t_users
ORDER BY id ${rule} <!-- 拼接 asc | desc -->
</select>
User user = new User(....);
List<User> ulist1 = userDAO.selectAllUsers1(user); //调用时传入user对象
List<User> ulist2 = userDao.selectAllUsers2("desc"); //调用时传入asc | desc
<select id="selectUsersByKeyword" resultType="user">
SELECT * FROM t_user
WHERE name = '${name}' <!-- 会存在注入攻击 比如传入参数是 【String name = "tom' or '1'='1";】-->
</select>
Lombok
简化类,通过注解快速实现类的getter和setter和构造和toString等方法
使用步骤:
Idea安装Lombok插件
file-->Settings-->Plugins-->搜索lombok
![](https://i-blog.csdnimg.cn/blog_migrate/f0bb387b63ec69392ff9bd51078dfcac.png)
依赖jar
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
编写类
使用注解:
@Data:自动实现 getter和setter 重写toString
@NoArgsConstructor 自动实现无参构造
@AllArgsConstructor 自动实现全参构造
Java标准类:Java Bean
1.属性私有
2.提供公有的getter和setter方法
3.提供无参构造函数