一、Mybatis的简介
1.1、MyBatis历史
MyBatis
最初是
Apache
的一个开源项目
iBatis
, 2010
年
6
月这个项目由
Apache Software Foundation
迁
移到了
Google Code
。随着开发团队转投
Google Code
旗下,
iBatis3.x
正式更名为
MyBatis
。代码于
2013
年
11
月迁移到
Github
。
iBatis
一词来源于
“internet”
和
“abatis”
的组合,是一个基于
Java
的持久层框架。
iBatis
提供的持久层框架
包括
SQL Maps
和
Data Access Objects
(
DAO
)。
1.2、MyBatis特性
1
)
MyBatis
是支持定制化
SQL
、存储过程以及高级映射的优秀的持久层框架
2
)
MyBatis
避免了几乎所有的
JDBC
代码和手动设置参数以及获取结果集
3
)
MyBatis
可以使用简单的
XML
或注解用于配置和原始映射,将接口和
Java
的
POJO
(
Plain Old Java
Objects
,普通的
Java
对象)映射成数据库中的记录
4
)
MyBatis
是一个 半自动的
ORM
(
Object Relation Mapping
)框架
二、搭建Mybatis
开发环境注意事项:MySQL 8版本使用jdbc8驱动,驱动类使用:com.mysql.cj.jdbc.Driver
1.创建maven项目:
①打包方式为jar
②引入依赖
<packaging>jar</packaging> <dependencies> <!-- Mybatis核心 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.10</version> </dependency> <!-- junit测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.16</version> </dependency> <!-- log4j日志 --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies>
2.创建mybatis核心配置文件
<?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文件,此后就可以在当前文件中使用${key}的方式访问value--> <properties resource="jdbc.properties"/> <!-- typeAliases:类型别名,即为某个具体的类型设置一个别名, 在Mybatis的范围中,就可以使用别名表示一个具体的类型--> <typeAliases> <!-- type:需要设置别名的类型 alias:设置某个类型的别名--> <!-- <typeAlias type="com.zhou.mybatis.pojo.User" alias="abc"></typeAlias>--> <!-- 不写alias默认有一个别名,即类名且不区分大小写,建议用--> <!-- <typeAlias type="com.zhou.mybatis.pojo.User" ></typeAlias>--> <!-- 通过包设置类型别名,指定包下所有的类型将全部拥有默认的别名,即类名且不区分大小写--> <package name="com.zhou.mybatis.pojo"/> </typeAliases> <!--设置连接数据库的环境--> <!-- environments:配置连接数据库的环境 --> <environments default="development"> <!-- environment:设置一个具体的连接数据库的环境 id为唯一标识,不可重复--> <environment id="development"> <!-- transactionManager:设置事务管理器 属性: type:设置事务管理的方式 type=“JDBC/MANAGED” JDBC:表示JDBC中原生的事务管理方式 MANAGED:被管理,例如Spring--> <transactionManager type="JDBC"/> <!-- dataSource:设置数据源 type:设置数据源的类型 type=“POOLED/UNPOOLED/JNDI” POOLED:表示使用数据库连接池 UNPOOLED:表示不使用数据库连接池 JNDI:表示使用上下文中的数据源--> <!-- <dataSource type="POOLED">--> <!-- <property name="driver" value="com.mysql.cj.jdbc.Driver"/>--> <!-- <property name="url"--> <!-- value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC"/>--> <!-- <property name="username" value="root"/>--> <!-- <property name="password" value="123456"/>--> <!-- </dataSource>--> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <!--引入mybatis的映射文件--> <mappers> <!-- <mapper resource="com/zhou/mybatis/mapper/UserMapper.xml"/>--> <!-- 以包的方式引入映射文件,但是必须满足两个条件: 1.mapper接口和映射文件所在的包必须一致 2.mapper接口的名字与映射文件的名字必须一致--> <package name="com.zhou.mybatis.mapper"/> </mappers> </configuration>
3.创建mapper接口
public interface
UserMapper
{
/**
*
添加用户信息
*/
int
insertUser
();
}
4.创建mybatis映射文件
1、映射文件的命名规则:
表所对应的实体类的类名+Mapper.xml
例如:表t_user,映射的实体类为User,所对应的映射文件为UserMapper.xml
因此一个映射文件对应一个实体类,对应一张表的操作
MyBatis映射文件用于编写SQL,访问以及操作表中的数据
MyBatis映射文件存放的位置是src/main/resources/mappers目录下
2、 MyBatis中可以面向接口操作数据,要保证两个一致:
a>mapper接口的全类名和映射文件的命名空间(namespace)保持一致
b>mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致
<mapper namespace="com.zhou.mybatis.mapper.UserMapper"> <!--int insertUser();--> <insert id="insertUser"> insert into t_user values(null,'周梓妍','041128',19,'女','123456@qq.com') </insert>
三、mybatis的增删改查
1.新增:
①mapper接口:
// 添加用户信息 int insertUser();
②映射文件:
<!--int insertUser();--> <insert id="insertUser"> insert into t_user values(null,'周梓妍','041128',19,'女','123456@qq.com') </insert>
2.删除:
①mapper接口:
// 删除用户信息 int deleteUser();
②映射文件:
<delete id="deleteUser"> delete from t_user where id=2; </delete>
3.修改:
①mapper接口:
// 修改用户信息 void updateUser();
②映射文件:
<!-- void updateUser();--> <update id="updateUser"> update t_user set username='周梓杰' where id=4; </update>
4.查询一个实体类对象:
①mapper接口:
//查询功能:根据id查询用户信息 User getUserById();
②映射文件:
<!-- resultType结果类型,查询出来的数据要转换为的实体类类型,resultMap自定义映射--> <select id="getUserById" resultType="com.zhou.mybatis.pojo.User"> select * from t_user where id=1; </select>
5.查询list集合
①mapper接口:
// 查询所有 List<User> getAllUser();
②映射文件:
<select id="getAllUser" resultType="com.zhou.mybatis.pojo.User"> select * from t_user; </select>
resultType:自动映射,用于属性名和表中字段名一致的情况
resultMap:自定义映射,用于一对多或多对一或字段名和属性名不一致的情况
四、mybatis获取参数值的两种方式
${}本质是字符串的拼接
#{}本质是占位符赋值
${}
使用字符串拼接的方式拼接
sql
,若为字符串类型或日期类型的字段进行赋值时,需要
手动加单 引号
;但是#{}
使用占位符赋值的方式拼接
sql
,此时为字符串类型或日期类型的字段进行赋值时,
可以
自动添加单引号
1.单个字面量类型的参数:
若mapper接口中的方法参数为单个字面量类型,
此时可以使用
${}
和
#{}
以任意的名称获取参数的值,注意
${}
需要手动加单引号
2.多个字面量类型的参数:
若
mapper
接口中的方法参数为多个时,
此时
MyBatis
会自动将这些参数放在一个
map
集合中,以
arg0,arg1...
为键,以参数为值;以
param1,param2...
为键,以参数为值;因此只需要通过
${}
和
#{}
访问
map
集合的键就可以获取相
对应的值,注意${}
需要手动加单引号
3.map集合类型的参数:
需要手动创建map集合,封装数据
Map<String, Object> map = new HashMap<String, Object>(); map.put("username", "周梓妍"); map.put("password", "041128");
4.实体类类型的参数:
若mapper接口中方法的参数类型为实体类类型,可以使用#{},${}通过实体类对象的属性名访问属性值,注${}需要手动添加引号
5.使用@Param注解标识参数
会将这些参数放在
map
集合中,以
@Param
注解的
value
属性值为键,以参数为值;
五、mybatis的各种查询功能
1.查询一个实体类对象
/**
*
根据用户
id
查询用户信息
* @param id
* @return
*/
User
getUserById
(
@Param
(
"id"
)
int
id
);
<!--User getUserById(@Param("id") int id);-->
<select
id
=
"getUserById"
resultType
=
"User"
>
select * from t_user where id = #{id}
</select>
2.查询list集合
/**
*
查询所有用户信息
* @return
*/
List
<
User
>
getUserList
();
<!--List<User> getUserList();-->
<select
id
=
"getUserList"
resultType
=
"User"
>
select * from t_user
</select>
3.查询单个数据
/**
*
查询用户的总记录数
* @return
*
在
MyBatis
中,对于
Java
中常用的类型都设置了类型别名
*
例如:
java.lang.Integer-->int|integer
*
例如:
int-->_int|_integer
*
例如:
Map-->map,List-->list
*/
int
getCount
();
<!--int getCount();-->
<select
id
=
"getCount"
resultType
=
"_integer"
>
select count(id) from t_user
</select>
4.查询一条数据为Map
/**
*
根据用户
id
查询用户信息为
map
集合
* @param id
* @return
*/
Map
<
String
,
Object
>
getUserToMap
(
@Param
(
"id"
)
int
id
);
<!--Map<String, Object> getUserToMap(@Param("id") int id);-->
<!--
结果:
{password=123456, sex=
男
, id=1, age=23, username=admin}-->
<select
id
=
"getUserToMap"
resultType
=
"map"
>
select * from t_user where id = #{id}
</select>
5.查询多条数据为Map
①一条数据产生一个map,多条数据产生多个map,将这些map集合放入list集合中
/**
*
查询所有用户信息为
map
集合
* @return
*
将表中的数据以
map
集合的方式查询,一条数据对应一个
map
;若有多条数据,就会产生多个
map
集合,此
时可以将这些
map
放在一个
list
集合中获取
*/
List
<
Map
<
String
,
Object
>>
getAllUserToMap
();
<!--Map<String, Object> getAllUserToMap();-->
<select
id
=
"getAllUserToMap"
resultType
=
"map"
>
select * from t_user
</select>
②使用@MapKey注解
/**
*
查询所有用户信息为
map
集合
* @return
*
将表中的数据以
map
集合的方式查询,一条数据对应一个
map
;若有多条数据,就会产生多个
map
集合,并
且最终要以一个
map
的方式返回数据,此时需要通过
@MapKey
注解设置
map
集合的键,值是每条数据所对应的
map
集合
*/
@MapKey
(
"id"
)
Map
<
String
,
Object
>
getAllUserToMap
();
<!--Map<String, Object> getAllUserToMap();-->
<!--
{
1={password=123456, sex=
男
, id=1, age=23, username=admin},
2={password=123456, sex=
男
, id=2, age=23, username=
张三
},
3={password=123456, sex=
男
, id=3, age=23, username=
张三
}
}
-->
<select
id
=
"getAllUserToMap"
resultType
=
"map"
>
select * from t_user
</select>
六、特殊SQL的执行
平时用#{}多一点,这些是使用${}的一些特例
1.模糊查询
List
<
User
>
testMohu
(
@Param
(
"mohu"
)
String
mohu
);
<!--List<User> testMohu(@Param("mohu") String mohu);-->
<select
id
=
"testMohu"
resultType
=
"User"
>
①<!--select * from t_user where username like '%${mohu}%'-->
②<!--select * from t_user where username like concat('%',#{mohu},'%')-->
③select * from t_user where username like "%"#{mohu}"%"
</select>
2.批量删除
int
deleteMore
(
@Param
(
"ids"
)
String
ids
);
<!--int deleteMore(@Param("ids") String ids);-->
<delete
id
=
"deleteMore"
>
delete from t_user where id in (${ids})
</delete>
3.设置表名
List
<
User
>
getAllUser
(
@Param
(
"tableName"
)
String
tableName
);
<!--List<User> getAllUser(@Param("tableName") String tableName);-->
<select
id
=
"getAllUser"
resultType
=
"User"
>
select * from ${tableName}
</select>
4.添加功能设置自增的主键
useGeneratedKeys:设置使用自增的主键
keyProperty:将获取的自增的主键放在传输的参数的某个属性值上(id)
<!--int insertUser(User user);-->
<insert
id
=
"insertUser"
useGeneratedKeys
=
"true"
keyProperty
=
"id"
>
insert into t_user values(null,#{username},#{password},#{age},#{sex})
</insert>
七、ResultMap自定义处理字段和属性的映射关系
resultMap:设置自定义映射关系
id:自定义映射的唯一标识
type:查询数据要映射的实体类类型
子标签:id:设置主键的映射关系
result:设置普通字段的映射关系
association:设置一对多的映射关系
collection:设置多对一的映射关系
property:设置映射关系中实体类中的属性名
column:设置映射关系中的字段名
若字段名和实体类中的属性名不一致,但是字段名符合数据库的规则(使用
_
),实体类中的属性
名符合
Java
的规则(使用驼峰)
此时也可通过以下两种方式处理字段名和实体类中的属性的映射关系:
①通过为字段起别名的方式
②配置全局变量,mapUnderscoreTocamelCase可以在查询表中数据是自动将_转换为驼峰类型
八、多对一的映射关系
1.级联方式处理映射关系
<resultMap
id
=
"empDeptMap"
type
=
"Emp"
>
<id
column
=
"eid"
property
=
"eid"
></id>
<result
column
=
"ename"
property
=
"ename"
></result>
<result
column
=
"age"
property
=
"age"
></result>
<result
column
=
"sex"
property
=
"sex"
></result>
<result
column
=
"did"
property
=
"dept.did"
></result>
<result
column
=
"dname"
property
=
"dept.dname"
></result>
</resultMap>
<!--Emp getEmpAndDeptByEid(@Param("eid") int eid);-->
<select
id
=
"getEmpAndDeptByEid"
resultMap
=
"empDeptMap"
>
select emp.*,dept.* from t_emp emp left join t_dept dept on emp.did =
dept.did where emp.eid = #{eid}
</select>
2.使用association处理映射关系
<resultMap
id
=
"empDeptMap"
type
=
"Emp"
>
<id
column
=
"eid"
property
=
"eid"
></id>
<result
column
=
"ename"
property
=
"ename"
></result>
<result
column
=
"age"
property
=
"age"
></result>
<result
column
=
"sex"
property
=
"sex"
></result>
<association property="dept" javaType="Dept">
<id
column
=
"did"
property
=
"did"
></id>
<result
column
=
"dname"
property
=
"dname"
></result>
</association>
</resultMap>
3.使用分步查询
Emp
getEmpByStep
(
@Param
(
"eid"
)
int
eid
);
<resultMap
id
=
"empDeptStepMap"
type
=
"Emp"
>
<id
column
=
"eid"
property
=
"eid"
></id>
<result
column
=
"ename"
property
=
"ename"
></result>
<result
column
=
"age"
property
=
"age"
></result>
<result
column
=
"sex"
property
=
"sex"
></result>
<!--
select
:设置分步查询,查询某个属性的值的
sql
的标识(
namespace.sqlId
)
column
:将
sql
以及查询结果中的某个字段设置为分步查询的条件
-->
<association
property
=
"dept"
select
=
"com.atguigu.MyBatis.mapper.DeptMapper.getEmpDeptByStep"
column
=
"did"
>
</association>
</resultMap>
<!--Emp getEmpByStep(@Param("eid") int eid);-->
<select
id
=
"getEmpByStep"
resultMap
=
"empDeptStepMap"
>
select * from t_emp where eid = #{eid}
</select>
九、一对多的映射关系
1.collection
ofType
:设置
collection
标签所处理的集合属性中存储数据的类型
2.分步查询
分步查询的优点:可以延迟加载
十、动态SQL
1.if
2.where
3.trim
trim
用于去掉或添加标签中的内容
常用属性:
prefix
:在
trim
标签中的内容的前面添加某些内容
prefixOverrides
:在
trim
标签中的内容的前面去掉某些内容
suffix
:在
trim
标签中的内容的后面添加某些内容
suffixOverrides
:在
trim
标签中的内容的后面去掉某些内容
4.choose,when,otherwise
5.foreach
6.SQL片段
<sql
id
=
"empColumns"
>
eid,ename,age,sex,did
</sql>
select <include refid="empColumns"><include />
from t_emp
十一、MyBatis的缓存
1.一级缓存
一级缓存是
SqlSession
级别的,通过同一个
SqlSession
查询的数据会被缓存,下次查询相同的数据,就
会从缓存中直接获取,不会从数据库重新访问
使一级缓存失效的四种情况:
1)
不同的
SqlSession
对应不同的一级缓存
2)
同一个
SqlSession
但是查询条件不同
3)
同一个
SqlSession
两次查询期间执行了任何一次增删改操作
4)
同一个
SqlSession
两次查询期间手动清空了缓存
2.二级缓存
二级缓存是
SqlSessionFactory
级别,通过同一个
SqlSessionFactory
创建的
SqlSession
查询的结果会被
缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取
二级缓存开启的条件:
a>
在核心配置文件中,设置全局配置属性
cacheEnabled="true"
,默认为
true
,不需要设置
b>
在映射文件中设置标签
<cache/>
c>
二级缓存必须在
SqlSession
关闭或提交之后有效
d>
查询的数据所转换的实体类类型必须实现序列化的接口
使二级缓存失效的情况:
两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
十二、Mybatis的逆向工程
①添加依赖和插件
<dependencies>
<dependency>
<groupId>
org.mybatis
</groupId>
<artifactId>
mybatis
</artifactId>
<version>
3.5.7
</version>
</dependency>
<!-- junit
测试
-->
<dependency>
<groupId>
junit
</groupId>
<artifactId>
junit
</artifactId>
<version>
4.12
</version>
<scope>
test
</scope>
</dependency>
<!-- log4j
日志
-->
<dependency>
<groupId>
log4j
</groupId>
<artifactId>
log4j
</artifactId>
<version>
1.2.17
</version>
</dependency>
<dependency>
<groupId>
mysql
</groupId>
<artifactId>
mysql-connector-java
</artifactId>
<version>
8.0.16
</version>
</dependency>
</dependencies>
<!--
控制
Maven
在构建过程中相关配置
-->
<build>
<!--
构建过程中用到的插件
-->
<plugins>
<!--
具体插件,逆向工程的操作是以构建过程中插件形式出现的
-->
<plugin>
<groupId>
org.mybatis.generator
</groupId>
<artifactId>
mybatis-generator-maven-plugin
</artifactId>
<version>
1.3.0
</version>
<!--
插件的依赖
-->
<dependencies>
<!--
逆向工程的核心依赖
-->
<dependency>
<groupId>
org.mybatis.generator
</groupId>
<artifactId>
mybatis-generator-core
</artifactId>
<version>
1.3.2
</version>
</dependency>
<!-- MySQL
驱动
-->
<dependency>
<groupId>
mysql
</groupId>
<artifactId>
mysql-connector-java
</artifactId>
<version>
8.0.16
</version>
</dependency>
</dependencies>
</plugin>
</plugins>
②创建
MyBatis
的核心配置文件
③创建逆向工程的配置文件
文件名必须是:
generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--
targetRuntime:
执行生成的逆向工程的版本
MyBatis3Simple:
生成基本的
CRUD
(清新简洁版)
MyBatis3:
生成带条件的
CRUD
(奢华尊享版)
-->
<context
id
=
"DB2Tables"
targetRuntime
=
"MyBatis3"
>
<!--
数据库的连接信息
-->
<jdbcConnection
driverClass
=
"com.mysql.cj.jdbc.Driver"
connectionURL
=
"jdbc:mysql://localhost:3306/mybatis?
serverTimezone=UTC"
userId
=
"root"
password
=
"123456"
>
</jdbcConnection>
<!-- javaBean
的生成策略
-->
<javaModelGenerator
targetPackage
=
"com.atguigu.mybatis.pojo"
targetProject
=
".\src\main\java"
>
<property
name
=
"enableSubPackages"
value
=
"true"
/>
<property
name
=
"trimStrings"
value
=
"true"
/>
</javaModelGenerator>
<!-- SQL
映射文件的生成策略
-->
<sqlMapGenerator
targetPackage
=
"com.atguigu.mybatis.mapper"
targetProject
=
".\src\main\resources"
>
<property
name
=
"enableSubPackages"
value
=
"true"
/>
</sqlMapGenerator>
<!-- Mapper
接口的生成策略
-->
<javaClientGenerator
type
=
"XMLMAPPER"
targetPackage
=
"com.atguigu.mybatis.mapper"
targetProject
=
".\src\main\java"
>
<property
name
=
"enableSubPackages"
value
=
"true"
/>
</javaClientGenerator>
<!--
逆向分析的表
-->
<!-- tableName
设置为
*
号,可以对应所有表,此时不写
domainObjectName -->
<!-- domainObjectName
属性指定生成出来的实体类的类名
-->
<table
tableName
=
"t_emp"
domainObjectName
=
"Emp"
/>
<table
tableName
=
"t_dept"
domainObjectName
=
"Dept"
/>
</context>
</generatorConfiguration>
十三、分页查询
1.插件的使用步骤
①添加依赖
<dependency>
<groupId>
com.github.pagehelper
</groupId>
<artifactId>
pagehelper
</artifactId>
<version>
5.2.0
</version>
</dependency>
②配置分页插件
在
MyBatis
的核心配置文件中配置插件
<plugins>
<!--
设置分页插件
-->
<plugin
interceptor
=
"com.github.pagehelper.PageInterceptor"
></plugin>
</plugins>
2.插件的使用
a>
在查询功能之前使用
PageHelper.startPage(int pageNum, int pageSize)
开启分页功能
pageNum
:当前页的页码
pageSize
:每页显示的条数
b>
在查询获取
list
集合之后,使用
PageInfo<T> pageInfo = new PageInfo<>(List<T> list, int
navigatePages)
获取分页相关数据
list
:分页之后的数据
navigatePages
:导航分页的页码数
c>
分页相关数据
PageInfo{
pageNum=8, pageSize=4, size=2, startRow=29, endRow=30, total=30, pages=8,
list=Page{count=true, pageNum=8, pageSize=4, startRow=28, endRow=32, total=30,
pages=8, reasonable=false, pageSizeZero=false},
prePage=7, nextPage=0, isFirstPage=false, isLastPage=true, hasPreviousPage=true,
hasNextPage=false, navigatePages=5, navigateFirstPage4, navigateLastPage8,
navigatepageNums=[4, 5, 6, 7, 8]
}
pageNum
:当前页的页码
pageSize
:每页显示的条数
size
:当前页显示的真实条数
total
:总记录数
pages
:总页数
prePage
:上一页的页码
nextPage
:下一页的页码
isFirstPage/isLastPage
:是否为第一页
/
最后一页
hasPreviousPage/hasNextPage
:是否存在上一页
/
下一页
navigatePages
:导航分页的页码数
navigatepageNums
:导航分页的页码,
[1,2,3,4,5]