什么是MyBatis?
MyBatis是一个优秀的持久层框架,它对jdbc的操作进行了封装,使得数据库的操作不再繁琐,避免大量的代码编写,使开发人员将更多的精力放在sql语句上。
MyBatis的优点
- 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件。
- 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。它只是一种帮助程序,让程序开发者自己设计数据库表结构和关系。每个sql语句可以独立配置,非常灵活。
- 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
- 提供映射标签,支持对象与数据库的ORM字段关系映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供xml标签,支持编写动态sql
Mybatis的简单使用
创建使用的数据库
create database mybatis;
use mybatis;
CREATE TABLE user (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
pwd VARCHAR(100) DEFAULT(NULL),
PRIMARY KEY (id)
)ENGINE=Innodb,CHARSET=UTF8;
insert into user(id,name,pwd) VALUES
(1,'孙悟空','666666'),
(2,'御坂美琴','233333'),
(3,'白井黑子','123456');
创建maven项目,并导入依赖
<dependencies>
<!-- MyBatis 核心依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version> <!-- 请使用最新版本 -->
</dependency>
<!-- 数据库驱动(例如 MySQL) -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version> <!-- 请根据数据库版本选择 -->
</dependency>
<!-- JUnit 5 核心依赖 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.0</version> <!-- 请使用最新版本 -->
<scope>test</scope>
</dependency>
<!-- JUnit 5 测试引擎 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
</dependencies>
创建实体类
package com.myLearning.pojo;
import java.io.Serializable;
public class User implements Serializable {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
在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">
<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://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 这里之后会配置 -->
<mapper resource="com/myLearning/Dao/UserMapper.xml"/>
</mappers>
</configuration>
创建一个用于获取SqlSession的工具类
package com.myLearning.util;
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 java.io.IOException;
import java.io.InputStream;
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
throw new RuntimeException(e);
}
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
创建Dao层的接口类
package com.myLearning.Dao;
import com.myLearning.pojo.User;
import java.util.List;
public interface UserDao {
List<User> getUserList();
}
创建Mapper层的映射文件UesrMapper.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">
<!--用于绑定刚刚创建的Dao(Mapper)接口-->
<mapper namespace="com.myLearning.Dao.UserDao">
<!-- 这里id填写接口中的方法,resultType填写集合中的泛型的类型-->
<select id="getUserList" resultType="com.myLearning.pojo.User">
select * from user
</select>
</mapper>
修改之前的mybatis-config.xml文件
在配置文件中配置Mapper的路径
<?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="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://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 加上刚刚创建的Mapper.xml路径 -->
<mapper resource="com/myLearning/Dao/UserMapper.xml"/>
</mappers>
</configuration>
修改pom.xml文件,防止资源导出时的问题
<!-- 在build中配置resources,防止资源导出时的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
编写测试类测试
package com.myLearning.Dao;
import com.myLearning.pojo.User;
import com.myLearning.util.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;
import java.util.List;
public class UserDaoTest {
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.getUserList();
for(User user : userList){
System.out.println(user);
}
sqlSession.close();
}
}
继续增加CRUD操作
修改数据库接口类(这里将UserDao.java重命名为UserMapper.java)
package com.myLearning.Dao;
import com.myLearning.pojo.User;
import java.util.List;
public interface UserMapper {
// 获取所有用户列表
List<User> getUserList();
// 插入一个新的用户
int insertUser(User user);
// 更新一个用户
int updateUser(User user);
// 删除一个用户
int deleteUser(int id);
}
修改UserMapper.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">
<!--用于绑定刚刚创建的Dao(Mapper)接口-->
<mapper namespace="com.myLearning.Dao.UserMapper">
<!-- 这里id填写接口中的方法,resultType填写集合中的泛型的类型-->
<select id="getUserList" resultType="com.myLearning.pojo.User">
select * from user
</select>
<insert id="insertUser" parameterType="com.myLearning.pojo.User">
insert into mybatis.user(id,name,pwd) values(#{id},#{name},#{pwd})
</insert>
<update id="updateUser" parameterType="com.myLearning.pojo.User">
update mybatis.user set pwd=#{pwd} where id=#{id}
</update>
<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id=#{id}
</delete>
</mapper>
测试
package com.myLearning.Dao;
import com.myLearning.pojo.User;
import com.myLearning.util.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;
import java.util.List;
public class UserMapperTest {
@Test
public void getUserListtest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserList();
for(User user : userList){
System.out.println(user);
}
sqlSession.close();
}
@Test
public void insertUserTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User(4,"上条当麻","123666");
int ret = mapper.insertUser(user);
System.out.println(ret);
// 增删改操作一定要提交事务!!!
sqlSession.commit();
sqlSession.close();
}
@Test
public void updateUserTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User(4,"上条当麻","123456789");
int ret = mapper.updateUser(user);
// 增删改需要提交事务
sqlSession.commit();
System.out.println(ret);
sqlSession.close();
}
@Test
public void deleteUserTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int ret = mapper.deleteUser(4);
sqlSession.commit();
System.out.println(ret);
sqlSession.close();
}
}
万能的Map
Map可以用作参数传递
添加UserMapper接口中的方法
// 更新一个用户(使用Map)
int updateUser2(Map<String,Object> map);
编写UserMapper.xml中的SQL语句
<update id="updateUser2" parameterType="map">
update mybatis.user set pwd=${mima} where id=${key}
</update>
测试
@Test
public void updateUserTest2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<>();
map.put("mima","233333333333");
map.put("key","4");
mapper.updateUser2(map);
sqlSession.commit();
sqlSession.close();
}
配置文件
核心配置文件
mybatis-config.xml,这是MyBatis的核心配置文件,它包含了MyBatis的所有全局配置信息,如数据库连接信息、事务管理、映射文件等。
Environment环境配置
MyBatis可以配置多个环境,每个环境对应一个数据库连接。在<environments>
标签中,可以配置多个<environment>
标签,每个<environment>
标签对应一个数据库连接。
<!-- 这里选择使用哪个环境配置 -->
<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://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED"><!-- 数据源配置 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
Properties配置
可以在核心配置文件中引入外部配置文件
编写一个db.properties文件
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8
username=root
passward=123456
在核心配置文件中引入外部配置文件
<?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>
<properties resource="db.properties">
<!-- 这里还可以继续添加更多的配置,但如果同名,优先使用配置文件中的配置-->
</properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${passward}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/myLearning/Dao/UserMapper.xml"/>
</mappers>
</configuration>
typeAliases别名配置
可以在核心配置文件中配置别名,方便使用
<typeAliases>
<typeAlias type="com.myLearning.pojo.User" alias="User"/>
</typeAliases>
也可以使用包扫描的方式,扫描指定包下的所有类,并自动为这些类注册别名,默认别名为类名,且不区分大小写,如果需要自定义,可以在类上使用@Alias注解自定义别名
<typeAliases>
<package name="com.myLearning.pojo"/>
</typeAliases>
使用别名
<insert id="insertUser" parameterType="user">
insert into mybatis.user(id,name,pwd) values(#{id},#{name},#{pwd})
</insert>
settings配置
常用的setting配置有:mapUnderscoreToCamelCase(开启驼峰命名映射),logImpl(指定日志实现),lazyLoadingEnabled(延迟加载),aggressiveLazyLoading(是否需要立即加载),cacheEnabled(是否开启二级缓存)
mappers配置
mappers配置主要用于指定mybatis映射文件的位置,主要有三种方式:
- 使用resource属性指定映射文件的位置
<mappers>
<mapper resource="com/myLearning/Dao/UserMapper.xml"/>
</mappers>
- 使用class属性指定接口的全类名,要求接口和映射文件同名,并且在同一目录下
<mappers>
<mapper class="com.myLearning.Dao.UserMapper"/>
</mappers>
- 使用package属性指定包名,要求接口和映射文件同名,并且在同一目录下
<mappers>
<package name="com.myLearning.Dao"/>
</mappers>
resultMap
resultMap放在Mapper.xml中进行配置,resultMap用于自定义结果集映射,可以解决实体类属性名和数据库表字段名不一致的问题。
假设数据库表user有id、name、pwd三个字段,实体类User有id、name、password三个属性,id和name属性名一致,password属性名和数据库表字段名不一致,可以使用resultMap自定义结果集映射。
<resultMap id="userMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="password" column="pwd"/>
</resultMap>
在select语句中可以使用resultMap属性指定resultMap的id。
<select id="getUserById" resultMap="userMap">
select * from user where id=#{id}
</select>
日志
mybatis内置了多种日志实现,可以通过在mybatis配置文件中配置logImpl属性来指定使用的日志实现,常用的日志实现有:
- SLF4J
- LOG4J
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING
- NO_LOGGING
在配置文件中配置logImpl属性
<settings>
<!-- 标准日志工厂实现 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
配置完后,mybatis会使用指定的日志实现来记录日志。
使用log4j2记录日志
导入log4j2的依赖
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.20.0</version>
</dependency>
在 src/main/resources 目录下创建一个 log4j2.xml 文件,配置 Log4j2 的日志输出
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" shutdownHook="disable">
<!-- 定义属性 -->
<Properties>
<!-- 日志文件输出目录 -->
<Property name="LOG_HOME">./logs/</Property>
<!-- 日志文件名称 -->
<Property name="LOG_FILE_NAME">test.log</Property>
<!-- 日志文件格式 -->
<Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Property>
</Properties>
<!-- 定义 Appenders -->
<Appenders>
<!-- 控制台输出 -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${LOG_PATTERN}"/>
</Console>
<!-- 滚动文件输出 -->
<RollingFile name="RollingFile" fileName="${LOG_HOME}/${LOG_FILE_NAME}"
filePattern="${LOG_HOME}/app-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<!-- 按时间滚动(每天) -->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!-- 按文件大小滚动(10MB) -->
<SizeBasedTriggeringPolicy size="10 MB"/>
</Policies>
<!-- 最多保留 30 个日志文件 -->
<DefaultRolloverStrategy max="30"/>
</RollingFile>
<!-- 异步日志 -->
<Async name="Async">
<AppenderRef ref="RollingFile"/>
</Async>
</Appenders>
<!-- 定义 Loggers -->
<Loggers>
<!-- 根日志记录器 -->
<Root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="Async"/>
</Root>
<!-- 自定义包或类的日志级别 -->
<Logger name="com.myapp" level="debug" additivity="false">
<AppenderRef ref="Console"/>
</Logger>
<!-- 禁用某些类的日志 -->
<Logger name="org.apache" level="off" additivity="false"/>
</Loggers>
</Configuration>
在mybatis-config.xml中配置log4j2
<settings>
<setting name="logImpl" value="LOG4J2"/>
</settings>
在代码中使用 Log4j2 记录日志
// 获取Logger对象
private static final Logger logger = LogManager.getLogger(UserMapper.class);
@Test
public void loggerTest(){
logger.info("测试log4j2的日志使用");
}
分页
分页在mybatis中可以通过sql实现,也可以通过rowbounds实现,现在基本不再使用rowbounds进行分页了
此外,还可以使用插件进行分页
使用注解开发
使用注解开发,可以减少配置文件,但是注解开发不能实现动态sql,所以注解开发一般用于简单的sql语句
简单示例
创建TestMapper接口
package com.myLearning.Dao;
import com.myLearning.pojo.User;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface TestMapper {
@Select("select * from user")
public List<User> getUsers();
}
在mybatis-config.xml中配置
<mappers>
<mapper resource="com/myLearning/Dao/UserMapper.xml"/>
<mapper class="com.myLearning.Dao.TestMapper"/>
</mappers>
测试
其他的增删改都是一样的,只是注解不同,@Insert、@Update、@Delete
关于@Param注解
当接口中的方法参数只有一个时,可以省略@Param注解,但是当接口中的方法参数有多个时且为基本类型或String类型时,必须使用@Param注解,否则会报错
Lombok的使用
Lombok是什么?
Lombok是一个Java库,可以自动生成一些常见的代码,比如getter、setter、toString、equals、hashCode等,可以减少代码量,提高开发效率
Lombok的使用
下载Lombok插件(新版IDEA已经内置了Lombok插件,无需下载)
添加依赖
在pom.xml中添加Lombok的依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
使用Lombok
在需要生成代码的类上添加注解,比如@ToString、@Getter、@Setter等,就可以自动生成对应的代码了
Lombok的注解
@Getter和@Setter
生成getter和setter方法
@ToString
生成toString方法
@EqualsAndHashCode
生成equals和hashCode方法
@NoArgsConstructor
生成无参构造方法
@AllArgsConstructor
生成全参构造方法
@Data
生成getter、setter、toString、equals、hashCode、无参构造方法
@Builder
生成Builder模式
@Slf4j
生成日志对象
多对一的sql查询(以多个Student与一个Teacher为例)
创建数据库表
create table student(
id int PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) not null,
tid int not null,
FOREIGN KEY (tid) REFERENCES teacher(id)
)ENGINE=INNODB,CHARSET=utf8;
CREATE table teacher(
id int PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) not null
)ENGINE=INNODB,CHARSET=utf8;
INSERT INTO teacher(id,name) VALUES(1,"利姆露老师");
INSERT INTO student(id,name,tid) VALUES(1,"三崎剑也",1);
INSERT INTO student(id,name,tid) VALUES(2,"克萝耶.欧贝鲁",1);
INSERT INTO student(id,name,tid) VALUES(3,"盖鲁·吉普森",1);
INSERT INTO student(id,name,tid) VALUES(4,"关口良太",1);
INSERT INTO student(id,name,tid) VALUES(5,"爱丽丝·伦德",1);
创建实体类
package com.myLearning.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student implements Serializable {
private int id;
private String name;
private Teacher teacher;
}
package com.myLearning.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Teacher implements Serializable {
private int id;
private String name;
}
创建Mapper接口
package com.myLearning.Dao;
import com.myLearning.pojo.Student;
import java.util.List;
public interface StudentMapper {
List<Student> getAllStudent();
}
package com.myLearning.Dao;
import com.myLearning.pojo.Teacher;
import java.util.List;
public interface TeacherMapper {
List<Teacher> getAllTeacher();
}
创建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">
<!--用于绑定刚刚创建的Dao(Mapper)接口-->
<mapper namespace="com.myLearning.Dao.StudentMapper">
<select id="getAllStudent" resultMap="StudentTeacher">
select * from student;
</select>
</mapper>
<?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">
<!--用于绑定刚刚创建的Dao(Mapper)接口-->
<mapper namespace="com.myLearning.Dao.TeacherMapper">
<select id="getAllTeacher" resultType="teacher">
select * from teacher;
</select>
</mapper>
在mybatis-config.xml中注册Mapper.xml
<mappers>
<mapper resource="com/myLearning/Dao/StudentMapper.xml"/>
<mapper resource="com/myLearning/Dao/TeacherMapper.xml"/>
</mappers>
为了成功完成Student类的创建,我们需要修改StudentMapper.xml,使用结果集映射的方法
这里使用多一种查询的方式完成,即在另一个查询中获得teacher的信息,然后使用resultMap进行映射,其中teacher的属性使用association进行映射
association中的select制定了查询teacher的sql语句,column制定了查询teacher的id,property制定了查询teacher的属性
<?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">
<!--用于绑定刚刚创建的Dao(Mapper)接口-->
<mapper namespace="com.myLearning.Dao.StudentMapper">
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" column="tid" javaType="Teacher" select="getAllTeacher"/>
</resultMap>
<select id="getAllStudent" resultMap="StudentTeacher">
select * from student;
</select>
<select id="getAllTeacher" resultType="Teacher">
select * from teacher where id=#{tid};
</select>
</mapper>
我们也有另一种方法完成任务,就是直接在一个select语句中完成查询,获得构建对象所有需要的信息,使用resultMap进行映射,其中teacher的属性使用association直接进行映射
<?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">
<!--用于绑定刚刚创建的Dao(Mapper)接口-->
<mapper namespace="com.myLearning.Dao.StudentMapper">
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
</association>
</resultMap>
<select id="getAllStudent" resultMap="StudentTeacher">
select s.id sid,t.id tid,s.name sname,t.name tname
from student s,teacher t
where s.tid = t.id;
</select>
</mapper>
一对多sql查询(以一个老师与多个学生举例)
修改实例类
package com.myLearning.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student implements Serializable {
private int id;
private String name;
private int tid;
}
package com.myLearning.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Teacher implements Serializable {
private int id;
private String name;
List<Student> students;
}
修改TeacherMapper接口
package com.myLearning.Dao;
import com.myLearning.pojo.Teacher;
public interface TeacherMapper {
Teacher getTeacherById(int id);
}
修改TeacherMapper.xml
第一种方法,使用嵌套查询的方法得到想要的结果
我们先查出指定id老师的信息,然后再利用这个信息去查询学生表得到学生信息,使用collection进行结果的映射
<?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">
<!--用于绑定刚刚创建的Dao(Mapper)接口-->
<mapper namespace="com.myLearning.Dao.TeacherMapper">
<select id="getTeacherById" resultMap="teacherWithStudents">
select * from teacher where id=#{id};
</select>
<select id="getAllStudent" resultType="Student">
select * from student where tid = #{id}
</select>
<resultMap id="teacherWithStudents" type="Teacher">
<collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getAllStudent"/>
</resultMap>
</mapper>
第二种方法,我们直接使用一条SQL语句查询出所需要的所有信息, 然后直接在resultMap中的collection内进行映射
<?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">
<!--用于绑定刚刚创建的Dao(Mapper)接口-->
<mapper namespace="com.myLearning.Dao.TeacherMapper">
<select id="getTeacherById" resultMap="teacherWithStudents">
select s.id sid, t.id tid, s.name sname, t.name tname from teacher t,student s where s.tid=t.id and t.id=#{id};
</select>
<resultMap id="teacherWithStudents" type="Teacher">
<result column="tid" property="id"/>
<result column="tname" property="name"/>
<collection property="students" ofType="Student">
<result column="sid" property="id"/>
<result column="sname" property="name"/>
<result column="tid" property="tid"/>
</collection>
</resultMap>
</mapper>
动态SQL
动态SQL是什么?
动态SQL指的是根据不同的条件生成不同的SQL语句
使用动态SQL
搭建环境
创建所需数据库
create table `blog`(
`id` VARCHAR(50) not null comment '博客ID',
`title` VARCHAR(100) not null,
`author` varchar(30) not null,
`create_time` datetime not null,
`views` int(30) not null
)ENGINE=InnoDB Default charset=utf8;
改动mybatis-config.xml
<settings>
<!-- 标准日志工厂实现 -->
<!-- <setting name="logImpl" value="STDOUT_LOGGING"/>-->
<setting name="logImpl" value="LOG4J2"/>
<!-- 开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
创建实体类
package com.myLearning.pojo;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class Blog implements Serializable {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
}
创建BlogMapper接口以及对应的xml文件,同时记得在mybatis-config.xml中注册
package com.myLearning.Dao;
import com.myLearning.pojo.Blog;
public interface BlogMapper {
int addBlog(Blog blog) ;
}
<?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">
<!--用于绑定刚刚创建的Dao(Mapper)接口-->
<mapper namespace="com.myLearning.Dao.BlogMapper">
<insert id="addBlog" parameterType="blog">
insert into blog(id,title,author,create_time,views)
values(#{id},#{title},#{author},#{createTime},#{views})
</insert>
</mapper>
插入数据
package com.myLearning.Dao;
import com.myLearning.pojo.Blog;
import com.myLearning.util.IDUtils;
import com.myLearning.util.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.Date;
public class BlogMapperTest {
@Test
public void addBlogTest() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Blog blog = new Blog();
blog.setId(IDUtils.getID());
blog.setAuthor("心理学家");
blog.setCreateTime(new Date());
blog.setViews(100);
blog.setTitle("思考,快与慢");
blogMapper.addBlog(blog);
blog.setId(IDUtils.getID());
blog.setAuthor("物理学家");
blog.setCreateTime(new Date());
blog.setViews(1000);
blog.setTitle("广义相对论");
blogMapper.addBlog(blog);
blog.setId(IDUtils.getID());
blog.setAuthor("哲学家");
blog.setCreateTime(new Date());
blog.setViews(10000);
blog.setTitle("马克思主义");
blogMapper.addBlog(blog);
blog.setId(IDUtils.getID());
blog.setAuthor("经济学家");
blog.setCreateTime(new Date());
blog.setViews(100000);
blog.setTitle("宏观经济学");
blogMapper.addBlog(blog);
blog.setId(IDUtils.getID());
blog.setAuthor("文学家");
blog.setCreateTime(new Date());
blog.setViews(1000);
blog.setTitle("雪国");
blogMapper.addBlog(blog);
sqlSession.commit();
sqlSession.close();
}
}
if标签的使用
Mapper内添加接口
List<Blog> getBlogIf(Map<String, Object> map);
xml实现
<select id="getBlogIf" parameterType="map" resultType="blog">
select * from blog where 1=1
<if test="title != null">
and title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</select>
测试类
@Test
public void testGetBlogIf() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("title", "雪国");
// map.put("author", "文学家");
List<Blog> blogs = blogMapper.getBlogIf(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
where标签的使用
where标签可以生成where语句,如果where语句接的第一个是and,会自动去掉第一个and
使用where标签改进刚刚的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">
<!--用于绑定刚刚创建的Dao(Mapper)接口-->
<mapper namespace="com.myLearning.Dao.BlogMapper">
<insert id="addBlog" parameterType="blog">
insert into blog(id,title,author,create_time,views)
values(#{id},#{title},#{author},#{createTime},#{views})
</insert>
<select id="getBlogIf" parameterType="map" resultType="blog">
select * from blog
<where>
<if test="title != null">
title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</where>
</select>
</mapper>
set标签的使用
set标签主要用于update语句,与where标签类似,set标签可以生成set语句,如果set标签生成的sql语句最后一个是‘,’会自动去掉最后一个‘,’
trim标签的使用
trim标签可以用于生成自定义的sql语句,where标签和set标签可以用trim标签替代
即trim标签可以生成where语句,也可以生成set语句
trim标签的属性:
- prefix:在trim标签生成的sql语句前加上指定的前缀
- suffix:在trim标签生成的sql语句后加上指定的后缀
- prefixOverrides:在trim标签生成的sql语句前去掉指定的前缀
- suffixOverrides:在trim标签生成的sql语句后去掉指定的后缀
choose标签的使用
choose标签类似于java中的switch语句,当choose标签中的when标签中的条件成立时,就会执行when标签中的sql语句,如果所有的when标签中的条件都不成立,就会执行otherwise标签中的sql语句
简单示例
Mapper类中添加方法
List<Blog> getBlogChoose(Map<String, Object> map);
Mapper.xml中添加sql语句
<select id="getBlogChoose" parameterType="map" resultType="blog">
select * from blog
<where>
<choose>
<when test="author != null">
author=#{author}
</when>
<when test="title != null">
title=#{title}
</when>
<otherwise>
views=#{views}
</otherwise>
</choose>
</where>
</select>
测试
@Test
public void getBlogChooseTest() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Map<String,Object> map = new HashMap<String,Object>();
// map.put("author","经济学家");
map.put("views",100);
List<Blog> blogs = blogMapper.getBlogChoose(map);
for(Blog blog : blogs){
System.out.println(blog);
}
sqlSession.close();
}
forEach标签
foreach标签主要用于循环遍历集合,常用于in条件中,如in (12,13,14), 可自行指定生成字符串的前缀、后缀、分隔符等。
简单示例
添加UserMapper接口中的方法
List<Blog> getBlogForeach(Map<String, Object> map);
在mapper.xml中添加sql语句,其中使用foreach标签实现遍历views集合,使得查询结果的views均在在设定的集合内
<select id="getBlogForeach" parameterType="map" resultType="blog">
select * from blog
<where>
<foreach collection="views" item="view" open="views in (" close=")"
separator=",">
#{view}
</foreach>
</where>
</select>
测试
@Test
public void getBlogForeachTest() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Map<String,Object> map = new HashMap<String,Object>();
// map.put("author","经济学家");
List<Integer> views = new ArrayList<Integer>();
views.add(100);
views.add(1000);
views.add(10000);
map.put("views", views);
List<Blog> blogs = blogMapper.getBlogForeach(map);
for(Blog blog : blogs){
System.out.println(blog);
}
sqlSession.close();
}
sql片段
sql片段可以用来定义可重用的sql语句,在需要使用的地方使用include标签引入
例如,我们可以使用sql片段将我们写的if标签放在sql片段中,然后在需要使用的地方使用include标签引入
<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
<select id="getBlogIf" parameterType="map" resultType="com.zhang.pojo.Blog">
select * from blog
<where>
<include refid="if-title-author"></include>
</where>
</select>
Mybatis缓存
缓存是什么?
在MyBatis中,缓存是一种用于提升数据库查询性能的机制,通过减少对数据库的直接访问来加快数据检索速度。
缓存的作用
缓存将查询结果存储在内存中,避免重复查询数据库,从而加快数据访问速度;减少数据库的访问次数,减轻数据库压力;直接从缓存获取数据,比从数据库查询更快。
MyBatis缓存分类
MyBatis提供了两种类型的缓存:一级缓存和二级缓存。
一级缓存
一级缓存是MyBatis在SqlSession级别维护的缓存,作用于SqlSession级别,默认情况下是开启的。当执行查询操作时,MyBatis会将查询结果存储在一级缓存中,当下一次相同的查询操作时,MyBatis会先从一级缓存中查找结果,如果找到了,就直接返回结果,避免了再次查询数据库。
一级缓存失效的情况
- 不同的SqlSession对应不同的一级缓存
- 同一个SqlSession但是查询条件不同
- 同一个SqlSession两次查询期间执行了增删改操作
- 同一个SqlSession两次查询期间手动清空了缓存,例如:sqlSession.clearCache();
二级缓存
二级缓存是MyBatis在SqlSessionFactory级别维护的缓存,作用于Mapper级别,默认情况下是关闭的。当执行查询操作时,MyBatis会先将查询结果放在一级缓存中,会话结束后,再存储在二级缓存中,当下一次相同的查询操作时,MyBatis会先从二级缓存中查找结果,如果找到了,就直接返回结果,避免了再次查询数据库。
二级缓存开启步骤
- 在MyBatis的配置文件中开启二级缓存配置
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
- 在Mapper文件中开启二级缓存配置
<cache/>
配置时可以设置的属性:
- eviction:缓存回收策略,默认为LRU(最近最少使用)
- flushInterval:缓存刷新间隔,默认为无,即没有刷新间隔
- size:缓存存放多少元素,默认为1024
- readOnly:是否只读,默认为false,即非只读,可读写
- 在需要使用二级缓存的方法上添加@CacheNamespace注解
@CacheNamespace(blocking = true)
public interface BlogMapper {
// ...
}
- 在实体类上添加@CacheNamespace注解
@CacheNamespace(blocking = true)
public class Blog {
// ...
}
二级缓存失效的情况
- 同一个Mapper中两次查询期间执行了增删改操作
- 同一个Mapper两次查询期间手动清空了缓存,例如:sqlSession.clearCache();
查询顺序
- 先查询二级缓存,二级缓存没有则查询一级缓存
- 一级缓存没有则查询数据库
自定义缓存
MyBatis允许自定义缓存,只需要实现Cache接口即可。除此之外,还可以使用第三方缓存,如Ehcache、Redis等。
自定义缓存实现步骤
- 实现Cache接口
public class MyCache implements Cache {
// ...
}
- 在Mapper文件中配置自定义缓存
<cache type="com.example.MyCache"/>
笔记总结于视频:https://www.bilibili.com/video/BV1NE411Q7Nx/?vd_source=16bf0c507e4a78c3ca31a05dff1bee4e