mybatis Nuyoah 2023-03-18 2024-03-12 MyBatis
对比
JDBC
SQL 夹杂在Java代码中耦合度高,导致硬编码内伤
维护不易且实际开发需求中 SQL 有变化,频繁修改的情况多见
代码冗长,开发效率低
Hibernate 和 JPA
操作简便,开发效率高
程序中的长难复杂 SQL 需要绕过框架
内部自动生产的 SQL,不容易做特殊优化
基于全映射的全自动框架,大量字段的 POJO 进行部分映射时比较困难。
反射操作太多,导致数据库性能下降
MyBatis
轻量级,性能出色
SQL 和 Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据
开发效率稍逊于HIbernate,但是完全能够接受
搭建Mybatis
引入MAVEN依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <dependencies > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.7</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > <scope > test</scope > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.30</version > </dependency > <dependency > <groupId > org.slf4j</groupId > <artifactId > slf4j-log4j12</artifactId > <version > 1.7.21</version > <scope > test</scope > </dependency > <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.17</version > </dependency > </dependencies >
创建MyBatis的核心配置文件
习惯上命名为mybatis-config.xml,这个文件名仅仅只是建议,并非强制要求。将来整合Spring
之后,这个配置文件可以省略,所以大家操作时可以直接复制、粘贴。
核心配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息
核心配置文件存放的位置是src/main/resources目录下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?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 /> <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/ssm?CharacterEncoding=true" /> <property name ="username" value ="root" /> <property name ="password" value ="admin" /> </dataSource > </environment > </environments > <mappers > <mapper resource ="mappers/UserMapper.xml" /> </mappers > </configuration >
创建Mapper接口
MyBatis中的mapper接口相当于以前的dao。但是区别在于,mapper仅仅是接口,我们不需要
提供实现类。
1 2 3 4 5 6 7 public interface UserMapper { int insertUser () ; }
创建MyBatis的映射文件
相关概念:ORM (O bject R elationship M apping)对象关系映射。
对象:Java的实体类对象
关系:关系型数据库
映射:二者之间的对应关系
Java概念
数据库概念
类
表
属性
字段/列
对象
记录/行
1、映射文件的命名规则:
表所对应的实体类的类名+Mapper.xml
例如:表t_user,映射的实体类为User,所对应的映射文件为UserMapper.xml
因此一个映射文件对应一个实体类,对应一张表的操作
MyBatis映射文件用于编写SQL,访问以及操作表中的数据
MyBatis映射文件存放的位置是src/main/resources/mappers目录下
2、 MyBatis中可以面向接口操作数据,要保证两个一致:
a>mapper接口的全类名和映射文件的命名空间(namespace)保持一致
b>mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?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" > <mapper namespace ="com.hgu.mybatis.mapper.UserMapper" > <insert id ="insertUser" > insert into t_user values (null,"admin","123456",23,'男',"1213456@qq.com") </insert > <update id ="updateUser" > UPDATE t_user SET username = "张三" WHERE id=1; </update > <select id ="getUserByID" resultType ="com.hgu.mybatis.pojo.User" > SELECT * FROM t_user WHERE id=1; </select > </mapper >
通过junit测试功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 InputStream is = Resources.getResourceAsStream("mybatis-config.xml" );SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder ();SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);SqlSession sqlSession = sqlSessionFactory.openSession(true );UserMapper userMapper = sqlSession.getMapper(UserMapper.class);映射文件中的SQL标签,并执行标签中的SQL语句 int result = userMapper.insertUser();System.out.println("结果:" +result);
SqlSession:代表Java程序和数据库 之间的会话 。(HttpSession是Java程序和浏览器之间的会话)
SqlSessionFactory:是“生产”SqlSession的“工厂”。
工厂模式:如果创建某一个对象,使用的过程基本固定,那么我们就可以把创建这个对象的
相关代码封装到一个“工厂类”中,以后都使用这个工厂类来“生产”我们需要的对象。
加入log4j日志功能
加入依赖
1 2 3 4 5 6 <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.17</version > </dependency >
加入log4j的配置文件
log4j的配置文件名为log4j.xml,存放的位置是src/main/resources目录下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE log4j :configuration SYSTEM "log4j.dtd" > <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(调试)
从左到右打印的内容越来越详细
SELECT语句
当我们使用SELECT语句的时候我们需要使用resultType或者resultMap来接收返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 <select id ="getUserByID" resultType ="com.hgu.mybatis.pojo.User" > SELECT * FROM t_user WHERE id=1; </select > <select id ="getAllUser" resultType ="com.hgu.mybatis.pojo.User" > SELECT * FROM t_user; </select >
核心配置文件详解
核心配置文件中的标签必须按照固定的顺序:
properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,refl
ectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 <?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 ="jdbc.properties" /> <typeAliases > <package name ="com.hgu.mybatis.pojo" /> </typeAliases > <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" /> <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 > <mappers > <package name ="com.hgu.mybatis.mapper" /> </mappers > </configuration >
MyBatis的增删改查
新增
1 2 3 4 <insert id ="insertUser" > insert into t_user values(null,'admin','123456',23,'男') </insert >
删除
1 2 3 4 <delete id ="deleteUser" > delete from t_user where id = 7 </delete >
修改
1 2 3 4 <update id ="updateUser" > update t_user set username='ybc',password='123' where id = 6 </update >
查询一个实体类对象
1 2 3 4 <select id ="getUserById" resultType ="com.atguigu.mybatis.bean.User" > select * from t_user where id = 2 </select >
查询list集合
1 2 3 4 <select id ="getUserList" resultType ="com.atguigu.mybatis.bean.User" > select * from t_user </select >
注意:
1、查询的标签select必须设置属性resultType或resultMap,用于设置实体类和数据库表的映射
关系
resultType:自动映射,用于属性名和表中字段名一致的情况
resultMap:自定义映射,用于一对多或多对一或字段名和属性名不一致的情况
MyBatis获取参数值的两种方式
MyBatis获取参数值的两种方式:${}和 #{}
${}的本质就是字符串拼接,#{}的本质就是占位符赋值
${}使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引
号;但是#{}使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,
可以自动添加单引号
单个字面量类型的参数
若mapper接口中的方法参数为单个的字面量类型
此时可以使用${}和#{}以任意的名称获取参数的值,注意${}需要手动加单引号
1 2 3 4 5 <select id ="getUserByID" resultType ="User" > # SELECT * FROM t_user WHERE id='${id}' SELECT * FROM t_user WHERE id=#{id}; </select >
多个字面量类型的参数
若mapper接口中的方法参数为多个时
此时MyBatis会自动将这些参数放在一个map集合中,以arg0,arg1…为键,以参数为值;以
param1,param2…为键,以参数为值;因此只需要通过${}和#{}访问map集合的键就可以获取相
对应的值,注意${}需要手动加单引号
1 2 3 4 <select id ="checkLogin" resultType ="User" > SELECT * FROM t_user WHERE id=#{arg0} AND username=#{arg1} </select >
map集合类型的参数
若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合,将这些数据放在
map中
只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号
1 2 3 4 <select id ="checkLoginByMap" resultType ="User" > SELECT * FROM t_user WHERE id=#{id} AND username=#{username} </select >
实体类类型的参数
若mapper接口中的方法参数为实体类对象时
此时可以使用${}和#{},通过访问实体类对象中的属性名获取属性值,注意${}需要手动加单引号
1 2 3 4 <select id ="checkLoginByMap" resultType ="User" > SELECT * FROM t_user WHERE id=#{id} AND username=#{username} </select >
使用@Param标识参数
可以通过@Param注解标识mapper接口中的方法参数
此时,会将这些参数放在map集合中,以@Param注解的value属性值为键,以参数为值;以
param1,param2…为键,以参数为值;只需要通过${}和#{}访问map集合的键就可以获取相对应
的值,
注意${}需要手动加单引号
1 2 3 4 <select id ="checkLoginByParam" resultType ="User" > SELECT * FROM t_user WHERE id=#{id} AND username=#{username} </select >
MyBatis的各种查询功能
查询一个实体类对象
1 2 3 4 5 6 User getUserById (@Param("id") int id) ;
1 2 3 4 <select id ="getUserById" resultType ="User" > select * from t_user where id = #{id} </select >
查询一个list集合
1 2 3 4 5 List<User> getUserList () ;
1 2 3 4 <select id ="getUserList" resultType ="User" > select * from t_user </select >
当查询的数据为多条时,不能使用实体类作为返回值,否则会抛出异常
TooManyResultsException;但是若查询的数据只有一条,可以使用实体类或集合作为返回值
查询单个数据
1 2 3 4 <select id ="getCount" resultType ="_integer" > select count(id) from t_user </select >
查询一条数据为map集合
1 2 3 4 5 6 Map<String, Object> getUserToMap (@Param("id") int id) ;
1 2 3 4 5 <select id ="getUserToMap" resultType ="map" > select * from t_user where id = #{id} </select >
查询多条数据为map集合
方式一:返回MapList集合
1 2 3 4 5 6 7 List<Map<String, Object>> getAllUserToMap () ;
1 2 3 4 <select id ="getAllUserToMap" resultType ="map" > select * from t_user </select >
方式二:返回Map集合,以我们查询结果中的一个值作为Key
1 2 3 4 5 6 7 8 9 @MapKey("id") Map<String, Object> getAllUserToMap () ;
1 2 3 4 5 6 7 8 9 10 11 <select id ="getAllUserToMap" resultType ="map" > select * from t_user </select >
特殊SQL的执行
模糊查询
模糊查询三种方式
‘%${mohu}%’
concat(’%’,#{mohu},’%’)
“%”#{mohu}"%"
错误的方式:
‘%#{mobu}%’,报错原因,在sql语句执行预编译的时候会将#{}转换为?,这样最后的语句是’%?%’,这样会导致?被识别为单纯的字符串,不再是占位符
1 2 3 4 5 6 List<User> testMohu (@Param("mohu") String mohu) ;
1 2 3 4 5 6 <select id ="testMohu" resultType ="User" > select * from t_user where username like "%"#{mohu}"%" </select >
批量删除
该方法不能使用#{} ,因为使用#的时候会将我们参数使用’‘链接起来,结果为delete from t_user where id in (‘1,2,3’)
1 2 3 4 5 6 int deleteMore (@Param("ids") String ids) ;
1 2 3 4 <delete id ="deleteMore" > delete from t_user where id in (${ids}) </delete >
动态设置表名
就是将表明作为参数传递进入Sql语句,不能够使用#{},因为#{}会自动添加‘’,但是sql语句中表名不能够使用‘’,所以我们必须使用${}
1 2 3 4 5 6 List<User> getAllUser (@Param("tableName") String tableName) ;
1 2 3 4 <select id ="getAllUser" resultType ="User" > select * from ${tableName} </select >
添加功能获取自增的主键
场景模拟:
t_clazz(clazz_id,clazz_name)
t_student(student_id,student_name,clazz_id)
1、添加班级信息
2、获取新添加的班级的id
3、为班级分配学生,即将某学的班级id修改为新添加的班级的id
1 2 3 4 5 6 7 8 int insertUser (User user) ;
1 2 3 4 <insert id ="insertUser" useGeneratedKeys ="true" keyProperty ="id" > insert into t_user values(null,#{username},#{password},#{age},#{sex}) </insert >
自定义映射resultMap
resultMap处理字段和属性的映射关系
若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <resultMap id ="userMap" type ="User" > <id property ="id" column ="id" > </id > <result property ="userName" column ="user_name" > </result > <result property ="password" column ="password" > </result > <result property ="age" column ="age" > </result > <result property ="sex" column ="sex" > </result > </resultMap > <select id ="testMohu" resultMap ="userMap" > select id,user_name,password,age,sex from t_user where user_name likeconcat('%',#{mohu},'%') </select >
若字段名和实体类中的属性名不一致,但是字段名符合数据库的规则(使用_),实体类中的属性
名符合Java的规则(使用驼峰)
此时也可通过以下两种方式处理字段名和实体类中的属性的映射关系
a>可以通过为字段起别名的方式 ,保证和实体类中的属性名保持一致
b>可以在MyBatis的核心配置文件中设置一个全局配置信息mapUnderscoreToCamelCase ,可
以在查询表中数据时,自动将_类型的字段名转换为驼峰
例如:字段名user_name,设置了mapUnderscoreToCamelCase,此时字段名就会转换为
userName
多对一映射处理
级联方式处理映射关系
1 2 3 4 5 6 7 8 9 10 11 12 <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 > <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 >
使用association处理映射关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <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 > <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 >
分步查询
查询员工信息
1 2 3 4 5 6 Emp getEmpByStep (@Param("eid") int eid) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <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 > <association property ="dept" select ="com.atguigu.MyBatis.mapper.DeptMapper.getEmpDeptByStep" column ="did" > </association > </resultMap > <select id ="getEmpByStep" resultMap ="empDeptStepMap" > select * from t_emp where eid = #{eid} </select >
根据员工所对应的部门id查询部门信息
1 2 3 4 5 6 Dept getEmpDeptByStep (@Param("did") int did) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?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" > <mapper namespace ="com.hgu.mybatis.mapper.DeptMapper" > <resultMap id ="DeptResultMapOne" type ="Dept" > <id property ="did" column ="did" > </id > <result property ="deptName" column ="dept_name" > </result > </resultMap > <select id ="getDeptByID" resultMap ="DeptResultMapOne" > SELECT * FROM t_dept WHERE did=#{did} </select > </mapper >
一对多映射处理
collection
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <resultMap id ="deptEmpMap" type ="Dept" > <id property ="did" column ="did" > </id > <result property ="dname" column ="dname" > </result > <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 > <select id ="getDeptEmpByDid" resultMap ="deptEmpMap" > select dept.*,emp.* from t_dept dept left join t_emp emp on dept.did =emp.did where dept.did = #{did} </select >
分步查询
第一步:查询部门信息
1 2 3 4 5 6 Dept getDeptByStep (@Param("did") int did) ;
1 2 3 4 5 6 7 8 9 10 <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 > <select id ="getDeptByStep" resultMap ="deptEmpStep" > select * from t_dept where did = #{did} </select >
根据部门id查询部门中的所有员工
1 2 3 4 5 6 List<Emp> getEmpListByDid (@Param("did") int did) ;
1 2 3 4 <select id ="getEmpListByDid" resultType ="Emp" > select * from t_emp where did = #{did} </select >
分步查询的优点:可以实现延迟加载
但是必须在核心配置文件中设置全局配置信息:
lazyLoadingEnabled :延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
aggressiveLazyLoading :当开启时,任何方法的调用都会加载该对象的所有属性 。否则,每个属性会按需加载此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和
collection中的fetchType属性设置当前的分步查询是否使用延迟加载 , fetchType="lazy(延迟加
载)|eager(立即加载)"
动态SQL
Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了
解决 拼接SQL语句字符串时的痛点问题。
if
if标签可通过test属性的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;反之
标签中的内容不会执行
1 2 3 4 5 6 7 8 9 10 11 12 13 <select id ="getEmpListByMoreTJ" resultType ="Emp" > select * from t_emp where 1=1 <if test ="ename != '' and ename != null" > and ename = #{ename} </if > <if test ="age != '' and age != null" > and age = #{age} </if > <if test ="sex != '' and sex != null" > and sex = #{sex} </if > </select >
where
where和if一般结合使用:
a>若where标签中的if条件都不满足,则where标签没有任何功能,即不会添加where关键字
b>若where标签中的if条件满足,则where标签会自动添加where关键字,并将条件最前方多余的
and去掉
注意:where标签不能去掉条件最后多余的and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <select id ="getEmpListByMoreTJ2" resultType ="Emp" > select * from t_emp <where > <if test ="ename != '' and ename != null" > ename = #{ename} </if > <if test ="age != '' and age != null" > and age = #{age} </if > <if test ="sex != '' and sex != null" > and sex = #{sex} </if > </where > </select >
trim
trim用于去掉或添加标签中的内容
常用属性:
prefix:在trim标签中的内容的前面添加某些内容
prefixOverrides:在trim标签中的内容的前面去掉某些内容
suffix:在trim标签中的内容的后面添加某些内容
suffixOverrides:在trim标签中的内容的后面去掉某些内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <select id ="getEmpListByMoreTJ" resultType ="Emp" > select * from t_emp <trim prefix ="where" suffixOverrides ="and" > <if test ="ename != '' and ename != null" > ename = #{ename} and </if > <if test ="age != '' and age != null" > age = #{age} and </if > <if test ="sex != '' and sex != null" > sex = #{sex} </if > </trim > </select >
choose、when、otherwise
choose、when、 otherwise相当于if…else if…else
when至少有一个,otherwise最多只能有一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <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 > </choose > </where > </select >
foreach
collection :设置需要循环的数组或集合
**item:**表示数组或集合中的每一个数据
**separator:**循环体之间的分隔符
**open:**foreach标签所循环的所有内容的开始符号
**close:**foreach标签中所循环的所有内容的结束符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <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 > <delete id ="deleteMoreByArray" > delete from t_emp where <foreach collection ="eids" item ="eid" separator ="or" > eid = #{eid} </foreach > </delete > <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标签进行引入
1 2 3 4 <sql id ="empColumns" > eid,ename,age,sex,did </sql > select <include refid ="empColumns" > </include > from t_emp
MyBatis的缓存
MyBatis的一级缓存
一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问
使一级缓存失效的四种情况:
不同的SqlSession对应不同的一级缓存
同一个SqlSession但是查询条件不同
同一个SqlSession两次查询期间执行了任何一次增删改操作
同一个SqlSession两次查询期间手动清空了缓存
MyBatis的二级缓存
二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被
缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取
二级缓存开启的条件:
a>在核心配置文件中,设置全局配置属性cacheEnabled=“true”,默认为true,不需要设置
b>在映射文件中设置标签
c>二级缓存必须在SqlSession关闭或提交之后有效
d>查询的数据所转换的实体类类型必须实现序列化的接口
使二级缓存失效的情况:
两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
二级缓存的相关配置
在mapper配置文件中添加的cache标签可以设置一些属性:
①eviction属性:缓存回收策略,默认的是 LRU。
LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。
FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
②flushInterval属性:刷新间隔,单位毫秒
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
③size属性:引用数目,正整数
代表缓存最多可以存储多少个对象,太大容易导致内存溢出
④readOnly属性:只读, true/false
true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了 很重要的性能优势。
false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是
false。
MyBatis缓存查询的顺序
先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
如果二级缓存没有命中,再查询一级缓存
如果一级缓存也没有命中,则查询数据库
SqlSession关闭之后,一级缓存中的数据会写入二级缓存
整合第三方缓存EHCache
添加依赖
1 2 3 4 5 6 7 8 9 10 11 12 <dependency > <groupId > org.mybatis.caches</groupId > <artifactId > mybatis-ehcache</artifactId > <version > 1.2.1</version > </dependency > <dependency > <groupId > ch.qos.logback</groupId > <artifactId > logback-classic</artifactId > <version > 1.2.3</version > </dependency >
各jar包功能
jar包名称
作用
mybatis-ehcache
Mybatis和EHCache的整合包
ehcache
EHCache核心包
slf4j-api
SLF4J日志门面包
logback-classic
支持SLF4J门面接口的一个具体实现
创建EHCache的配置文件ehcache.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?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 >
设置二级缓存的类型
1 <cache type ="org.mybatis.caches.ehcache.EhcacheCache" />
加入logback日志
存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志。 创建logback的配置文件logback.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?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 > <root level ="DEBUG" > <appender-ref ref ="STDOUT" /> </root > <logger name ="com.atguigu.crowd.mapper" level ="DEBUG" /> </configuration >
EHCache配置文件说明
属性名
是否必须
作用
maxElementsInMemory
是
在内存中缓存的element的最大数目
maxElementsOnDisk
是
在磁盘上缓存的element的最大数目,若是0表示无穷大
eternal
是
设定缓存的elements是否永远不过期。 如果为true,则缓存的数据始终有效, 如果为false那么还要根据timeToIdleSeconds、timeToLiveSeconds判断
overflowToDisk
是
设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
timeToIdleSeconds
否
当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时, 这些数据便会删除,默认值是0,也就是可闲置时间无穷大
timeToLiveSeconds
否
缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB
否
DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区
diskPersistent
否
在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
diskExpiryThreadIntervalSeconds
否
磁盘缓存的清理线程运行间隔,默认是120秒。每个120s, 相应的线程会进行一次EhCache中数据的清理工作
memoryStoreEvictionPolicy
否
当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。 默认是LRU (最近最少使用),可选的有LFU (最不常使用)和FIFO (先进先出)
MyBatis的逆向工程
正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。 Hibernate是支持正向工
程的。
逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:
Java实体类
Mapper接口
Mapper映射文件
创建逆向工程的步骤
添加依赖和插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 <?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 > org.example</groupId > <artifactId > SSM</artifactId > <version > 1.0-SNAPSHOT</version > <packaging > pom</packaging > <modules > <module > mybatis_demo01</module > <module > mybatis_demo02</module > <module > mybatis_demo03</module > </modules > <properties > <maven.compiler.source > 17</maven.compiler.source > <maven.compiler.target > 17</maven.compiler.target > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > </properties > <dependencies > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.7</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > <scope > test</scope > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.30</version > </dependency > <dependency > <groupId > org.slf4j</groupId > <artifactId > slf4j-log4j12</artifactId > <version > 1.7.21</version > <scope > test</scope > </dependency > <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.17</version > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.mybatis.generator</groupId > <artifactId > mybatis-generator-maven-plugin</artifactId > <version > 1.4.2</version > <dependencies > <dependency > <groupId > org.mybatis.generator</groupId > <artifactId > mybatis-generator-core</artifactId > <version > 1.4.2</version > </dependency > <dependency > <groupId > com.mchange</groupId > <artifactId > c3p0</artifactId > <version > 0.9.5.5</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.30</version > </dependency > </dependencies > </plugin > </plugins > </build > </project >
创建MyBatis的核心配置文件
创建逆向工程的配置文件
文件名必须是:generatorConfig.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <?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 > <context id ="DB2Tables" targetRuntime ="MyBatis3" > <jdbcConnection driverClass ="com.mysql.cj.jdbc.Driver" connectionURL ="jdbc:mysql://localhost:3306/ssm?CharacterEncoding=true" userId ="root" password ="admin" > </jdbcConnection > <javaModelGenerator targetPackage ="com.hgu.mybatis.pojo" targetProject =".\src\main\java" > <property name ="enableSubPackages" value ="true" /> <property name ="trimStrings" value ="true" /> </javaModelGenerator > <sqlMapGenerator targetPackage ="com.hgu.mybatis.mapper" targetProject =".\src\main\resources" > <property name ="enableSubPackages" value ="true" /> </sqlMapGenerator > <javaClientGenerator type ="XMLMAPPER" targetPackage ="com.hgu.mybatis.mapper" targetProject =".\src\main\java" > <property name ="enableSubPackages" value ="true" /> </javaClientGenerator > <table tableName ="t_emp" domainObjectName ="Emp" /> <table tableName ="t_dept" domainObjectName ="Dept" /> </context > </generatorConfiguration >
执行MBG插件的generate目标
效果
QBC查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Test public void testMBG () { try { InputStream is = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ().build(is); SqlSession sqlSession = sqlSessionFactory.openSession(true ); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); mapper.updateByPrimaryKeySelective(newEmp(1 ,"admin" ,22 ,null ,"456@qq.com" ,3 )); } catch (IOException e) { e.printStackTrace(); } }
分页插件
limit index,pageSize
pageSize:每页显示的条数
pageNum:当前页的页码
index:当前页的起始索引,index=(pageNum-1)*pageSize
count:总记录数
totalPage:总页数
totalPage = count / pageSize;
if(count % pageSize != 0){
totalPage += 1;
}
pageSize=4,pageNum=1,index=0 limit 0,4
pageSize=4,pageNum=3,index=8 limit 8,4
pageSize=4,pageNum=6,index=20 limit 8,4
首页 上一页 2 3 4 5 6 下一页 末页
分页插件的使用步骤
添加依赖
1 2 3 4 5 <dependency > <groupId > com.github.pagehelper</groupId > <artifactId > pagehelper</artifactId > <version > 5.2.0</version > </dependency >
配置分页插件
在MyBatis的核心配置文件中配置插件
1 2 3 4 <plugins > <plugin interceptor ="com.github.pagehelper.PageInterceptor" > </plugin > </plugins >
分页插件的使用
a>在查询功能之前使用PageHelper.startPage(int pageNum, int pageSize)开启分页功能
pageNum:当前页的页码
pageSize:每页显示的条数
b>在查询获取list集合之后,使用PageInfo pageInfo = new PageInfo<>(List 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]