文章目录
MyBatis
MyBatis简介
MaBatis特性
1)是支持定制化SQL,存储过程以及高级映射的优秀的持久层框架
2)避免了几乎所有的JDBC代码和手动设置参数以及获取结果集
3)可以使用简单的xml或注解用于配置和原始映射,将接口和java的POJO映射成数据库中的记录
4)是一个半自动的ORM框架
MaBatis下载
https://github.com/mybatis/mybatis-3
下载导入maven依赖即可,这里主要下载官方文档
和其他持久化层技术对比
JDBC
- SQL夹杂在Java代码中耦合度高,导致硬编码内伤
- 维护不易且SQL有变化,需要频繁修改
- 代码冗长,开发效率低
Hibernate和JPA
- 操作简便,开发效率高
- 程序中长难复杂SQL需要绕过框架
- 内部自动生产的SQL,不容易做特殊优化
- 基于全映射的全自动框架,大量字段POJO进行映射时比较困难
- 反射操作太多,导致数据库性能下降
MyBatis
- 轻量级,性能出色
- SQL和java编码分开,功能边界清晰了,java代码专注业务,SQL语句专注数据
- 开发效率稍逊于HIbernate,但是完全能够接受
搭建MyBatis
开发环境
maven3.8.8
mysql8.0
mybatis3.5.14
创建maven工程
打包方式:jar
引入依赖
<!-- Mybatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.14</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>
创建MyBatis核心配置文件
主要用于配置连接数据库的环境以及Mybatis的全局配置信息,在src/main/resources目录下,习惯将其命名为mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 引入文件-->
<!-- 引入文件后下面数据库相关设置可以这样写 <property name="driver" value="${jdbc.driver}"/>-->
<properties resource="jdbc.properties"></properties>
<!-- 标签顺序:
properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,
objectWrapperFactory?,reflectorFactory?,plugins?,environments?,
databaseIdProvider?,mappers?-->
<!--给全类名设置别名-->
<typeAliases>
<!-- 给type设置别名为alias的值-->
<typeAlias type="com.zhe.pojo.User" alias="User"></typeAlias>
</typeAliases>
<!-- 配置连接数据库的环境-->
<!-- environments:配置多个数据库的环境
属性:default:设置默认使用的环境id
-->
<environments default="development">
<!-- environment:配置某个具体的环境
属性:id:表示连接数据库的环境的唯一标识,不能重复
-->
<environment id="development">
<!--
transactionManager:设置事务管理方式
属性:
type:设置事务管理方式,type="JDBC|MANAGED"
type="JDBC":设置当前环境的事务管理都必须手动处理
type="MANAGED":设置事务被管理,例如spring中的AOP
-->
<transactionManager type="JDBC"/>
<!--
dataSource:设置数据源
属性:
type:设置数据源的类型,type="POOLED|UNPOOLED|JNDI"
type="POOLED":使用数据库连接池缓存数据库连接,下次使用可以从缓存中直接获取,不需要重新创建
type="UNPOOLED":不使用数据库连接池,即每次使用连接都需要重新创建
type="JNDI":调用上下文中的数据源
-->
<dataSource type="POOLED">
<!-- 设置连接数据库的驱动-->
<!-- mysql8需要加上cj-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<!-- 设置连接数据库的连接地址-->
<!-- mysql8需要在最后配置时区-->
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC"/>
<!-- 设置连接数据库的连接用户名-->
<property name="username" value="root"/>
<!-- 设置连接数据库的连接密码-->
<property name="password" value="dyz200472"/>
</dataSource>
</environment>
</environments>
<!-- 引入映射文件-->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
<typeAliases>标签,设置类型的别名
标签顺序
properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?
两个子标签
1)<typeAlias>,type属性写全类名,alias属性如果设置别名,不设置alias属性,该类型拥有默认别名,即类名不区分大小写
2)<package>标签,name属性写包的路径,表示以包为单位,将包下所有的类型设置类型别名,即类名不区分大小写
设置数据库环境第二种方式
1)将这些驱动等写到jdbc.properties文件中,文件放在resources目录下
文件内容,写jdbc.driver是为了以后区分文件方便
2)在配置文件中引入jdbc.properties
<!-- 引入文件-->
<properties resource="jdbc.properties"></properties>
3)修改配置代码
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<mappers>标签引入映射文件
方式一:一个一个进行引入
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
方式二:以包为单位
<mappers>
<!--
以包为单位引入映射文件
注意:在resources下创建包要用/分隔而不是.分隔
要求
1.mapper接口所在的包要和映射文件所在的包一致
2.mapper接口要和映射文件名字一致
-->
<package name="com.zhe.mapper"/>
</mappers>
创建Mapper接口
//有几张表就有几个接口
public interface UserMapper {
int insertUser();
}
创建MyBatis映射文件
ORM:对象关系映射
- 对象:Java的实体类对象
- 关系:关系型数据库
- 映射:二者之间的关系
MaBatis映射文件
1)每一张表对应一个实体类对应一个映射文件,映射文件命名规则:表所对应的实体类的类名+Mapper.xml
2)用于编写sql
3)存放位置:src/main/resources/mappers目录
<?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">
<!--映射文件的namespace和mapper接口全类名保持一致-->
<mapper namespace="com.zhe.mapper.UserMapper">
<!--映射文件中sql语句的id与mapper接口中方法名保持一致-->
<insert id="insertUser">
insert into t_user values(null,'admin','12345',23,'男','12345@qq.com')
</insert>
</mapper>
编写测试代码
public class MaBatisTest {
@Test
public void testMabatis() throws IOException {
//加载核心配置文件,以字节输入流方式获取
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
//获取SqlSessionFactoryBuilder
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取SqlSessionFactory
SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
//获取sqlSession
SqlSession sqlSession = build.openSession(true);//默认不自动提交,若需要自动提交事务,增加参数true
//获取mapper接口对象 获取mapper接口底层是代理模式
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//测试,返回值为受影响的行数
int i = mapper.insertUser();
//提交事务
//sqlSession.commit();
System.out.println(i);
}
}
SqlSession:代表java程序和数据库之间的会话
SqlSessionFactory:是生产SqlSession的工厂
加入log4j日志功能
加入依赖
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
加入log4j配置文件log4j.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<!--信息爆红没关系hh-->
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS}
%m (%F:%L) \n" />
</layout>
</appender>
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="info" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>
日志的级别
FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试)
级别由高到低,打印内容越来越详细
项目结构
MyBatis增删改查
实现增删改查的基本步骤
mapper接口中写抽象方法->MaBatis映射文件中编写sql->测试类中进行测试
增删改查,重点在于MaBatis映射文件,下面都是映射文件中的代码
增
<!--int insertUser();-->
<insert id="insertUser">
insert into t_user values(null,'admin','123456',23,'男')
</insert>
删
<!--int deleteUser();-->
<delete id="deleteUser">
delete from t_user where id = 7
</delete>
改
<!--int updateUser();-->
<update id="updateUser">
update t_user set username='ybc',password='123' where id = 6
</update>
查
查询实体类对象
<!-- User getUserById(); -->
<select id="getUserById" resultType="com.zhe.pojo.User">
select * from t_user where id = 3
</select>
查询集合
<!-- List<User> getAllUser(); 注释里都是对应的mapper接口的抽象方法-->
<select id="getAllUser" resultType="User">
select * from t_user
</select>
注意
1)查询功能必须设置resultType或resultType,表示查询结果返回值类型
resultType 自动映射 字段名和属性名一致的情况使用
resultType 自定义映射 字段名和属性名不一致或者多对一,一对多的情况
(也可以不设置,我没有设置没报错,应该是版本更新之后不需要设置,默认为自动映射)
2)查询数据为多条时,方法不能使用实体类作为返回值,只能使用集合
MyBatis获取参数的两种方式(重点)
${}
本质是字符串拼接,使用时需要手动添加单引号
elect * from t_user where username = '${username}'
#{}
本质是占位符赋值,不需要添加单引号
select * from t_user where username = #{username}
MyBatis获取参数值的各种情况(重点)
单个字面量类型的参数
若mapper接口中的方法参数为单个的字面量类型,此时可以使用${}和#{}以任意的名称获取参数的值
多个字面量类型的参数
此时MaBatis会自动将这些参数放在一个map集合中,以arg0,arg1…为键,以参数为值;以param1,param2…为键,以参数为值;因此只需通过${}和#{}访问map集合的键就可以获取对应的值
map集合类型的参数
手动创建map集合,只需要通过${}和#{}直接访问map集合的键就可以获取对应的值
实体类型的参数
参数为实体类对象,通过${}和#{}访问实体类对象中的属性名获取属性值
使用@Param标识参数
使用@Param注解标识mapper接口中的方法参数
//命名参数
User checkLoginByParam(@Param("username") String username,@Param("password") String password);
此时,会将这些参数放在map集合中,以@Param注解的value属性值为键,以参数为值;以param1,param2为键,以参数为值;只需要通过${}和#{}访问map集合的键就可以获取对应的值
总结
整合为实体类对象类型,map集合参数和**@Param**两种情况即可
前两种均可以使用@Param方式获取参数对应的值
MyBatis的各种查询功能
查询一个实体类对象
mapper接口中的方法
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>
查询一个list集合
mapper接口中的方法
List<User> getUserList();
映射文件代码
<!--List<User> getUserList();-->
<select id="getUserList" resultType="User">
select * from t_user
</select>
查询单个数据
mapper接口中的方法
int getCount();
映射文件代码
<!--int getCount();-->
<select id="getCount" resultType="_integer">
select count(id) from t_user
</select>
在MaBatis中,对于java常用的类型都设计了类型别名
例如:java.lang.Integer–>int,integer
int–>_int,_integer
Map–>map
List–>list
查询一条数据为map集合
mapper接口中的方法
Map<String, Object> getUserToMap(@Param("id") int id);
映射文件代码
<!--Map<String, Object> getUserToMap(@Param("id") int id);-->
<select id="getUserToMap" resultType="map">
select * from t_user where id = #{id}
</select>
<!--结果:{password=123456, sex=男, id=1, age=23, username=admin}-->
查询多条数据为map集合
方式一:将表中数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,此时可以将这些map放在一个list集合中获取
mapper接口中的方法
List<Map<String, Object>> getAllUserToMap()
映射文件代码
<!--List<Map<String, Object>> getAllUserToMap()-->
<select id="getAllUserToMap" resultType="map">
select * from t_user
</select>
方式二:将表中数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,并且最终要以一个map的方式返回数据,此时需要通过@MapKey注解设置map集合的键,值是每条数据所对应的map集合
mapper接口中的方法
@MapKey("id")
Map<String, Object> getAllUserToMap();
映射文件代码
<!--Map<String, Object> getAllUserToMap();-->
<select id="getAllUserToMap" resultType="map">
select * from t_user
</select>
结果:
<!--
{
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=张三}
}-->
特殊SQL的执行
模糊查询
<!--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>
批量删除
<!--int deleteMore(@Param("ids") String ids);-->
<delete id="deleteMore">
delete from t_user where id in (${ids})
</delete>
动态设置表名
<!--List<User> getAllUser(@Param("tableName") String tableName);-->
<select id="getAllUser" resultType="User">
select * from ${tableName}
</select>
添加功能获取自增的主键
useGeneratedKeys:设置当前Sql使用自增的主键
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处理字段和属性的映射关系
查询操作时,数据库中字段名与实体类中属性名不一致,查询结果为null
解决方案
1.给字段设置别名
select eid,emp_name empName,age,sex,email from t_emp
2.设置全局配置
在MaBatis核心配置文件中设置一个全局配置信息mapUnderscoreToCamelCase,可以在查询表中数据时,自动将_类型的字段名转换为驼峰
例如:字段名user_name设置了mapUnderscoreToCamelCase,此时字段名就会转换为userName
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
3.通过restultMap设置自定义映射
<!--
resultMap:设置自定义映射
属性:
id:表示自定义映射的唯一标识
type:查询的数据要映射的实体类的类型
子标签:
id:设置主键的映射关系
result:设置普通字段的映射关系
association:设置多对一的映射关系
collection:设置一对多的映射关系
属性:
property:设置映射关系中实体类中的属性名
column:设置映射关系中表中的字段名
-->
<resultMap id="empResultMap" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
</resultMap>
<select id="getAllEmp" resultMap="empResultMap">
select * from t_emp
</select>
多对一映射处理
查询员工信息以及员工所对应的部门信息
多个员工对应一个部门
级联方式处理映射关系
<resultMap id="empDeptMap" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<!--表示将查询结果中的 "did" 列的值映射到 Emp 对象中的 dept 对象的 did 属性。-->
<result property="dept.did" column="did"></result>
<result property="dept.deptName" column="dept_name"></result>
</resultMap>
<!--Emp getEmpAndDeptByEid(@Param("eid") int eid);-->
<select id="getEmpAndDeptByEid" resultMap="empDeptMap">
select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid = #{eid}
</select>
使用association处理映射关系
<resultMap id="empDeptMap" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<!--
association:处理多对一的映射关系
property:需要处理多对的映射关系的实体类中的属性属性名
javaType:该属性的类型
-->
<association property="dept" javaType="Dept">
<id property="id" column="id"></id>
<result property="deptName" column="dept_name"></result>
</association>
</resultMap>
<!--Emp getEmpAndDeptByEid(@Param("eid") int eid);-->
<select id="getEmpAndDeptByEid" resultMap="empDeptMap">
select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid = #{eid}
</select>
分步查询
1)查询员工信息
/**
* 通过分步查询查询员工信息
*
* EmpMapper接口
*/
Emp getEmpByStep(@Param("eid") int eid);
<!-- EmpMapper.xml配置文件 -->
<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>
<!--
property:将查询结果映射到dept属性
select:设置分步查询,查询某个属性的值的sql的标识(namespace.sqlId) 就是全类名.查询部门id的方法
column:将sql以及查询结果中的某个字段设置为分步查询的条件-->
<association property="dept"
select="com.zhe.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>
2)根据员工所对应的部门id查询部门信息
/**
* 分步查询的第二步:根据员工所对应的did查询部门信息
* DeptMapper接口
*
*/
Dept getEmpDeptByStep(@Param("did") int did);
<!-- DeptMapper.xml配置文件 -->
<!--Dept getEmpDeptByStep(@Param("did") int did);-->
<select id="getEmpDeptByStep" resultType="Dept">
select * from t_dept where did = #{did}
</select>
一对多映射处理
根据部门id查询部门以及部门中的员工信息
collection
/**
* 根据部门id查询部门以及部门中的员工信息
*
*
*/
Dept getDeptEmpByDid(@Param("did") int did);
<resultMap id="deptEmpMap" type="Dept">
<id property="did" column="did"></id>
<result property="dname" column="dname"></result>
<!--
ofType:设置collection标签所处理的集合属性中存储数据的类型-->
<collection property="emps" ofType="Emp">
<id property="eid" column="eid"></id>
<result property="ename" column="ename"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
</collection>
</resultMap>
<!--Dept getDeptEmpByDid(@Param("did") int did);-->
<select id="getDeptEmpByDid" resultMap="deptEmpMap">
select * from t_dept left join t_emp on t_dept.did =
t_emp.did where t_dept.did = #{did}
</select>
分步查询
1)查询部门信息
/**
* 分步查询部门和部门中的员工
* DeptMapper接口
*
*/
Dept getDeptByStep(@Param("did") int did);
<resultMap id="deptEmpStep" type="Dept">
<id property="did" column="did"></id>
<result property="dname" column="dname"></result>
<collection property="emps" fetchType="eager"
select="com.atguigu.MyBatis.mapper.EmpMapper.getEmpListByDid" column="did">
</collection>
</resultMap>
<!--Dept getDeptByStep(@Param("did") int did);-->
<select id="getDeptByStep" resultMap="deptEmpStep">
select * from t_dept where did = #{did}
</select>
2)根据部门id查询部门中所有员工
/**
* 根据部门id查询员工信息
*
* EmpMapper接口
*/
List<Emp> getEmpListByDid(@Param("did") int did);
<!--List<Emp> getEmpListByDid(@Param("did") int did);-->
<select id="getEmpListByDid" resultType="Emp">
select * from t_emp where did = #{did}
</select>
延迟加载
分步查询的优点:可以实现延迟加载,但是必须在核心配置文件中设置全局配置信息
<settings>
<!-- 开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
lazyLoadingEnabled:延迟加载的开关,当开启时,所有的关联对象都会延迟加载
aggressiveLazyLoading:默认为false关闭,当开启时,任何方法的调用都会加载该对象的所有属性,否则每个属性会按需加载
默认属性就可以实现按需加载,此时可通过可通过association和 collection中的fetchType属性,,fetchType=“lazy(延迟加 载)|eager(立即加载)”
动态SQL
是一种根据特定条件动态拼装SQL语句的功能,它的存在是为了解决拼接SQL语句字符串时的痛点问题
if
test属性内表达式的结果为true,则标签中的内容会执行,反之标签中的内容不会执行
<select id="getEmpByConditionOne" resultType="Emp">
select * from t_emp where 1=1 and
<if test="empName != null and empName != '' ">
emp_name = #{empName}
</if>
<if test="age != null and age != '' ">
and age = #{age}
</if>
</select>
where
where标签一般和if结合使用
1)若where标签中的if条件都不满足,则where标签没有任何功能,即不会添加where关键字
2)若where标签中if条件满足,则where标签自动添加where关键字,并将条件前面多余的and或者or去掉
注意:无法去掉后面多余的and或者or
<select id="getEmpByConditionTwo" resultType="Emp">
select * from t_emp
<where>
<!--
where
有内容时,自动生成where关键字,并且将内容前多余的and或or去掉
当where标签中没有内容时,此时where标签没有任何效果
-->
<if test="empName != null and empName != '' ">
emp_name = #{empName}
</if>
<if test="age != null and age != '' ">
and age = #{age}
</if>
</where>
</select>
trim
trim标签用于去掉或者添加标签中的内容
常用属性
prefix:在trim标签的内容的前面添加某些内容
prefixOverrides:在前面去掉某些内容
suffix:在trim标签的内容的后面添加某些内容
suffixOverrides:在后面去掉某些内容
<select id="getEmpByCondition" resultType="Emp">
select * from t_emp
<trim prefix="where" suffixOverrides="and|or">
<if test="empName != null and empName != '' ">
emp_name = #{empName} and
</if>
<if test="age != null and age != '' ">
age = #{age}
</if>
</trim>
</select>
choose,when,otherwise
1)相当于if…else,有一个when执行了其他就都不执行了
2)when至少有一个,otherwise最多只能有一个
<select id="getEmpListByChoose" resultType="Emp">
select <include refid="empColumns"></include> from t_emp
<where>
<choose>
<when test="ename != '' and ename != null">
ename = #{ename}
</when>
<when test="age != '' and age != null">
age = #{age}
</when>
<when test="sex != '' and sex != null">
sex = #{sex}
</when>
<when test="email != '' and email != null">
email = #{email}
</when>
<!--相当于else-->
<otherwise>
did = 2
</otherwise>
</choose>
</where>
</select>
foreach
就是for循环了啦
属性
collection:设置要循环的数组或集合
item:表示数组或集合中的每一个数据
separator:设置循环体之间的分隔符
open:设置foreach标签中内容的开始符
close:设置foreach标签中的内容的结束符
<!--int insertMoreEmp(List<Emp> emps);-->
<insert id="insertMoreEmp">
insert into t_emp values
<foreach collection="emps" item="emp" separator=",">
(null,#{emp.ename},#{emp.age},#{emp.sex},#{emp.email},null)
</foreach>
</insert>
<!--int deleteMoreByArray(int[] eids);-->
<delete id="deleteMoreByArray">
delete from t_emp where
<foreach collection="eids" item="eid" separator="or">
eid = #{eid}
</foreach>
</delete>
<!--int deleteMoreByArray(int[] eids);-->
<delete id="deleteMoreByArray">
delete from t_emp where eid in
<foreach collection="eids" item="eid" separator="," open="(" close=")">
#{eid}
</foreach>
</delete>
SQL片段
sql片段标签,可以记录一段公共sql片段,在使用的地方通过include标签进行引入
<sql id="empColumns">
eid,ename,age,sex,did
</sql>
select <include refid="empColumns"></include> from t_emp
MyBatis的缓存
MyBatis的一级缓存
一级缓存的级别
一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问
一级缓存的失效情况
使一级缓存失效的四种情况:
1)不同的SqlSession对应不同的一级缓存
2)同一个SqlSession但是查询条件不同
3)同一个SqlSession两次查询期间执行了任何一次增删改操作
4)同一个SqlSession两次查询期间调用sqlSession.clearCache()手动清空了缓存
MyBatis的二级缓存
二级缓存的级别
二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存,下次若再执行相同的查询语句,结果就会从缓存中获取
二级缓存开启的条件
1)核心配置文件中,设置全局配置属性cacheEnabled=“true”,默认就为true,故不需要设置
2)在映射文件中设置标签<cache/>
3)查询的数据所转换的实体类类型必须实现序列化的接口即implements Serializable
4)二级缓存必须在SqlSession关闭或提交之后有效
二级缓存失效的情况
两次查询之间进行任意的增删改,会使一级和二级缓存同时失效
二级缓存相关配置
在mapper映射文件中添加的cache标签的属性
1)eviction:缓存回收策略
LRU(默认)
FIFO
SOFT
WEAK
2)flushInterval:刷新间隔,单位毫秒
3)size:缓存最多可以存储多少个对象
4)readOnly:是否只读
true:只读缓存,效率高
false:读写缓存,效率低但是安全,默认为false
缓存查询的顺序
二级缓存->一级缓存->数据库
注意:SqlSession关闭之后会将一级缓存中的数据存到二级缓存中
整合第三方缓存EHCache(了解)
添加依赖
<!-- Mybatis EHCache整合包 -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
<!-- slf4j日志门面的一个具体实现 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
各jar包功能
创建EHCache配置文件ehcache.xml
<?xml version="1.0" encoding="utf-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 磁盘保存路径 -->
<diskStore path="D:\atguigu\ehcache"/>
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
配置文件说明(了解)
设置二级缓存的类型
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
加入logback日志
创建logback配置文件logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- 指定日志输出的位置 -->
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 日志输出的格式 -->
<!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger]
[%msg]%n</pattern>
</encoder>
</appender>
<!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
<!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
<root level="DEBUG">
<!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
<appender-ref ref="STDOUT" />
</root>
<!-- 根据特殊需求指定局部日志级别 -->
<logger name="com.atguigu.crowd.mapper" level="DEBUG"/>
</configuration>
MyBatis逆向工程
神魔是逆向工程
正向工程:先创建java实体类,由框架负责根据实体类生成数据库表
逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成java实体类,mapper接口,mapper映射文件
创建逆向工程的步骤
添加依赖和插件
<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>com.zhe</groupId>
<artifactId>MaBatis04_MBG</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>MaBatis04_MBG</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- 依赖MyBatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.14</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- 逆向工程的核心依赖 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
</dependencies>
<!-- 控制Maven在构建过程中相关配置 -->
<build>
<!-- 构建过程中用到的插件 -->
<plugins>
<!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.0</version>
</plugin>
</plugins>
</build>
</project>
创建MyBatis核心配置文件mybatis-config.xml
创建逆向工程的配置文件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="dyz200472">
</jdbcConnection>
<!-- javaBean的生成策略-->
<javaModelGenerator targetPackage="com.zhe.pojo"
targetProject=".\src\main\java">
<!--是否能使用子包-->
<property name="enableSubPackages" value="true" />
<!--字段名前面有空格的话,可以自动去掉-->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- SQL映射文件的生成策略 -->
<sqlMapGenerator targetPackage="com.zhe.mapper"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- Mapper接口的生成策略 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.zhe.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>
执行插件generate目标
QBC风格的增删改查
public class MBGTest {
@Test
public void testMBG() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//查询所有数据
// List<Emp> list = mapper.selectByExample(null);
// list.forEach(emp -> System.out.println(emp));
//根据条件查询
EmpExample example = new EmpExample();
//名字为彭于晏,年龄大于等于5
example.createCriteria().andEmpNameEqualTo("彭于晏").andAgeGreaterThanOrEqualTo(5);
//前面的条件加上或者名字为段元哲
example.or().andEmpNameEqualTo("段元哲");
List<Emp> list = mapper.selectByExample(example);
System.out.println(list);
//普通修改,如果有的设置为null,则修改为null
mapper.updateByPrimaryKey(new Emp(3,"admin",23,null,"123",2));
//选择修改,如果有的设置为null,则不会修改,会维持原状
mapper.updateByPrimaryKeySelective(new Emp(3,"admin",23,null,"123",2));
}
}
小插曲:报错,maven死活导入不进去依赖
在复制老师配置的时候总是有依赖死活导入不进去,原来是整了两个dependencies标签,导致只有一个生效,因此以后依赖都要放在一个<dependencies>里面哦
分页插件
分页插件使用步骤
添加依赖
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
配置分页插件
在MaBatis的核心配置文件中配置插件
<plugins>
<!--设置分页插件-->
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
分页插件的使用
1)在查询之前开启分页
PageHelper.startPage(1,2);
pageNum:当前页的页码
pageSize:每页显示的条数
2)在查询之后获取分页相关数据
PageInfo<Emp> page = new PageInfo<>(list, 3);
list:分页之后的数据
navigatePages:导航分页的页码数
代码实现
public class PageHelperTest {
/**
* mysql分页操作
* limit index,pageSize
* index:当前页的起始索引
* pageSize:每页显示的条数
* pageNum:当前页的页码
* index=(pageNum-1)* pageSize
*/
@Test
public void testPageHelper() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//使用分页插件实现分页功能
//1.在查询功能之前开启分页
PageHelper.startPage(1,2);
List<Emp> list = mapper.selectByExample(null);
list.forEach(emp -> System.out.println(emp));
//2.在查询之后获取相关的数据
//list表示分页数据,3表示当前导航分页的数量
PageInfo<Emp> page = new PageInfo<>(list, 3);
System.out.println(page);
}
}
分页相关常用数据
pageNum:当前页的页码
pageSize:每页显示的条数
size:当前页显示的真实条数
total:总记录数
pages:总页数
prePage:上一页的页码
nextPage:下一页的页码
isFirstPage/isLastPage:是否为第一页/最后一页
hasPreviousPage/hasNextPage:是否存在上一页/下一页
navigatePages:导航分页的页码数
navigatepageNums:导航分页的页码