本篇文章是观看B站UP主狂神说关于Mybatis讲解的视频,借鉴Mybatis官方文档以及CSDN博主黑心白莲相关文章整理的个人Mybatis笔记。因笔者能力有限,文章难免有错误之处,欢迎各位评论勘误,共同探讨!
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
Maven仓库
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.7version>
dependency>
中文文档:链接
GitHub:链接
搭建数据库
新建项目
创建一个普通的 Maven 项目
删除 src 目录(可将此项目作为父项目,然后创建子项目)
在 pom.xml 文件中导入 Maven 依赖
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.25version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.6version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
dependencies>
右键项目名创建一个模块 Module
编写 Mybatis 的核心配置文件(mybatis-config.xml)
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/数据库名?useSSL=true&useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="数据库用户名"/>
<property name="password" value="数据库密码"/>
dataSource>
environment>
environments>
configuration>
编写 Mybatis 工具类文件(MybatisUtils.java)
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
//使用Mybatis获取SqlSessionFactory对象
static {
InputStream inputStream = null;
try {
String resource = "mybatis-config.xml";
inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
*/
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
实体类 pojo / Bean
Dao 接口 (UserMapper.java)
public interface UserMapper {
/**
* 通过用户ID查询用户
* @param id 用户ID
* @return 用户所有信息
*/
User getUserById(int id);
}
接口实现文件(UserMapper.xml)
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sit.mapper.UserMapper">
<select id="getUserById" parameterType="int" resultType="com.sit.pojo.User">
select * from mybatis.user where id = #{id}
select>
mapper>
测试
junit测试
@Test
public void getUserById() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
sqlSession.close();
}
可能遇到的问题
org.apache.ibatis.binding.BindingException: Type interface com.sit.mapper.UserMapper is not known to the MapperRegistry.
解决方法:
在 pop.xml 文件中添加如下代码:
<build>
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
UserMapper.xml 文件中 namespace 的包名 需要和 Dao/Mapper 接口的包名一致!
<mapper namespace="com.sit.mapper.UserMapper">.....mapper>
public interface UserMapper {
/**
* 获得用户列表
* @return 用户列表
*/
List<User> getUserList();
}
在对应的 Mapper.xml 文件中编写SQL语句
<select id="getUserList" resultType="com.sit.pojo.User">
select * from mybatis.user
select>
测试
@Test
public void getUserList() {
//1.获取sqlSession
SqlSession sqlSession = MybatisUtils.getSqlSession();
//2.执行sql语句
try{
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
} finally {
//3.关闭sqlSession
sqlSession.close();
}
}
接口中添加方法
/**
* 添加用户
* @param user 新用户信息
* @return 是否插入成功
*/
int addUser(User user);
在对应的 Mapper.xml 文件中编写SQL语句
<insert id="addUser" parameterType="com.sit.pojo.User">
insert into mybatis.user(id, name, pwd) VALUES (#{id}, #{name}, #{pwd})
insert>
测试
@Test
public void addUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res = mapper.addUser(new User(4, "小王", "666666"));
if (res > 0) System.out.println("用户添加成功! ");
//增删改必须提交事务
sqlSession.commit();
sqlSession.close();
}
接口中添加方法
/**
* 通过用户ID更新用户信息
* @param user 新的用户信息
* @return 是否修改成功
*/
int updateUserById(User user);
在对应的 Mapper.xml 文件中编写SQL语句
<update id="updateUserById" parameterType="com.sit.pojo.User">
update mybatis.user set name = #{name}, pwd = #{pwd} where id = #{id}
update>
测试
@Test
public void updateUserById() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res = mapper.updateUserById(new User(5, "小杨", "987654"));
if(res > 0) {
System.out.println("用户修改成功! ");
}
//增删改必须提交事务
sqlSession.commit();
sqlSession.close();
}
接口中添加方法
/**
* 通过用户ID删除用户信息
* @param id 用户ID
* @return 是否成功删除
*/
int deleteUserById(int id);
在对应的 Mapper.xml 文件中编写SQL语句
<delete id="deleteUserById" parameterType="int">
delete from mybatis.user where id = #{id}
delete>
测试
@Test
public void deleteUserById() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res = mapper.deleteUserById(4);
if(res > 0) {
System.out.println("用户删除成功! ");
}
//增删改都需要提交事务
sqlSession.commit();
sqlSession.close();
}
增删改操作一定要提交事务!!!
sqlSession.commit();
在Java代码执行的时候,传递通配符% %
List<User> userList = mapper.getUserLike("%王%");
在sql拼接中使用通配符
select * from user where name like "%"#{value}"%"
当实体类的属性或数据库中的字段过多时,应当考虑使用Map.
UserMapper.java
//用万能 Map 插入用户
public void addUser2(Map<String,Object> map);
UserMapper.xml
<insert id="addUser2" parameterType="map">
insert into user (id,name,password) values (#{userid},#{username},#{userpassword})
insert>
测试
@Test
public void addUser2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("userid",4);
map.put("username","小王");
map.put("userpassword","789789");
mapper.addUser2(map);
//提交事务
sqlSession.commit();
//关闭资源
sqlSession.close();
}
mybatis-config.xml
Mybatis的配置文件包含了Mybatis行为的设置和属性信息。
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
编写一个配置文件 db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/数据库名?userSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username=用户名
password=密码
在核心配置文件中引入
<properties resource="db.properties">
<property name="username" value="用户名"/>
<property name="password" value="密码"/>
properties>
注意点:如果外部文件(例如:db.properties)和核心配置文件(mybatis-config.xml)有相同字段,则优先使用外部配置文件中的属性值。
<typeAliases>
<typeAlias type="com.sit.pojo.User" alias="User"/>
typeAliases>
也可以指定一个包,每一个在包 com.sit.pojo 中的 Java Bean, 在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。例如 com.sit.pojo.User 的别名为 user ; 若有注解,则别名为其注解值。
<typeAliases>
<package name="com.sit.pojo"/>
typeAliases>
<settings>
<setting name="logImpl" value="LOG4J"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="cacheEnabled" value="true"/>
settings>
MapperRegistry:注册绑定 Mapper 文件;
方式一:使用 xml 文件绑定注册【推荐使用】
<mappers>
<mapper resource="com/sit/mapper/UserMapper.xml"/>
mappers>
方式二:使用 class 文件绑定注册
<mappers>
<mapper class="com.sit.mapper.UserMapper"/>
mappers>
注意点:
方式三:使用包扫描进行注入
<mappers>
<package name="com.sit.mapper"/>
mappers>
作用域和生命周期是至关重要的,错误地使用会导致严重的并发问题。
SqlSessionFactoryBuilder:
SqlSessionFactory:
SqlSession:
图示中每一个 Mapper 代表一个具体的业务!
如果一个数据库操作出现了异常,日志是排错的最好助手!
曾经:sout、debug
现在:日志工厂
在 mybatis-config.xml 的 Settings 中设置具体使用的日志
STDOUT_LOGGING 标准日志工厂的实现
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
什么是 Log4j ?
先在 pop.xml 文件中导入 LOG4J 的依赖包
<dependencies>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
dependencies>
在 resources 文件夹下建立 log4j.properties 文件进行配置
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/log4j.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][20%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
在 mybatis-config.xml 核心配置文件中配置 Log4j 为日志工厂的实现
<settings>
<setting name="logImpl" value="LOG4J"/>
settings>
Log4j 的测试运行
简单使用
在要使用 Log4j 的测试类中,导入包 org.apache.log4j.Logger
日志对象,参数为当前类的class
static Logger logger = Logger.getLogger(UserMapperTest.class);
日志级别
logger.info("info: 进入了testLog4j");
logger.debug("debug: 进入了testLog4j");
logger.error("erro: 进入了testLog4j");
之后可在 log 文件夹中查看日志文件信息
语法:SELECT * from tableName limit startIndex,pageSize
例如:SELECT * from user limit 3 #[0,n-1]
接口
List<User> getUsersByLimit(Map<String,Integer> map);
Mapper.xml
<select id="getUsersByLimit" parameterType="map" resultMap="UserMap">
select * from mybatis.user limit #{startIndex},#{pageSize}
select>
测试
@Test
public void getUsersByLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> userList = mapper.getUsersByLimit(map);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
接口
List<User> getUsersByRowBounds();
Mapper.xml
<select id="getUsersByRowBounds" resultMap="UserMap">
select * from mybatis.user
select>
测试
@Test
public void getUsersByRowBounds(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//RowBounds实现
RowBounds rowBounds = new RowBounds(0, 2);
//通过java代码层面实现分页
List<User> userList = sqlSession.selectList("com.sit.mapper.UserMapper.getUsersByRowBounds",null,rowBounds);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
动态SQL就是 指根据不同的条件生成不同的SQL语句。
所谓的动态SQL,本质上还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码。
CREATE TABLE `mybatis`.`blog` (
`id` VARCHAR(10) NOT NULL AUTO_INCREMENT COMMENT '博客id',
`title` varchar(30) NOT NULL COMMENT '博客标题',
`author` varchar(30) NOT NULL COMMENT '博客作者',
`create_time` datetime NOT NULL COMMENT '创建时间',
`views` int(30) NOT NULL COMMENT '浏览量',
PRIMARY KEY (`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8;
创建一个基础项目
导包
编写配置文件
编写实体类
package com.sit.pojo;
import java.util.Date;
/**
* @version : v1.0
* @function : 博客类
*/
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
public Blog() {
}
public Blog(String id, String title, String author, Date createTime, int views) {
this.id = id;
this.title = title;
this.author = author;
this.createTime = createTime;
this.views = views;
}
//省略各个属性的 get() 和 set() 方法
@Override
public String toString() {
return "Blog{" +
"id=" + id +
", title='" + title + '\'' +
", author='" + author + '\'' +
", createTime=" + createTime +
", views=" + views +
'}';
}
}
编写实体类对应Mapper接口和Mapper.XML文件
<select id="queryBlogIF" parameterType="map" resultType="Blog">
select * from mybatis.blog where 1=1
<if test="title != null">
and title = #{title}
if>
<if test="author != null">
and author = #{author}
if>
select>
<select id="queryBlogsByChoose" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<choose>
<when test="title != null">
title=#{title}
when>
<when test="author != null">
and author=#{author}
when>
<otherwise>
views=#{views}
otherwise>
choose>
where>
select>
<select id="queryBlogsByIf" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<if test="title != null">
and title = #{title}
if>
<if test="author != null">
and author = #{author}
if>
where>
select>
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title != null">title=#{title},if>
<if test="author != null">author=#{author},if>
set>
where views=#{views}
update>
开启日志
在一个 SqlSession 中查询两次相同的记录
@Test
public void testCacheLevel1() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
System.out.println("===================================");
User user2 = mapper.getUserById(1);
System.out.println(user2);
System.out.println("===================================");
System.out.println(user == user2);
System.out.println("===================================");
sqlSession.close();
}
查询不同的数据
执行了增删改操作(由于会改变原有数据,所以必定会刷新缓存!)
查询不同的 Mapper.xml
手动清理缓存
@Test
public void testCacheLevel1() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
System.out.println("===================================");
//手动清理缓存
sqlSession.clearCache();
User user2 = mapper.getUserById(1);
System.out.println(user2);
System.out.println("===================================");
System.out.println(user == user2);
System.out.println("===================================");
sqlSession.close();
}
小结:
一级缓存默认是开启的,只在一次 SqlSession 中有效,也就是拿到连接到关闭连接这个阶段,一级缓存相当于一个 Map。
在 mybatis-config.xml 开启全局缓存
<settings>
<setting name="cacheEnabled" value="true"/>
settings>
在要使用二级缓存的 mapper.xml 中开启
<cache/>
也可以自定义参数
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
测试
如果没有自定义参数则会报错,需要将实体类序列化!
Cause: java.io.NotSerializableException: com.sit.pojo.User
解决方案
public class User implements Serializable {
...}
小结:
数据库中的字段
新建项目中 POJO 的实体类属性
public class User {
private int id;
private String name;
private String password;
}
测试出现的问题
解决方法
起别名
<select id="getUserById" parameterType="int" resultType="user">
select id,name,pwd as password from mybatis.user where id = #{id}
select>
结果集映射(ResultMap)
<resultMap id="UserMap" type="User">
<result column="id" property="id" />
<result column="name" property="name" />
<result column="pwd" property="password" />
resultMap>
<select id="getUserById" parameterType="int" resultMap="UserMap">
select * from mybatis.user where id = #{id}
select>
在 pop.xml 的 build 中配置 resources
<build>
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
结果集映射(ResultMap)
<resultMap id="UserMap" type="User">
<result column="id" property="id" />
<result column="name" property="name" />
<result column="pwd" property="password" />
resultMap>
<select id="getUserById" parameterType="int" resultMap="UserMap">
select * from mybatis.user where id = #{id}
select>
在 pop.xml 的 build 中配置 resources
<build>
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>