Framework
三层架构详解见:MVC模式与三层架构的关系

在mybatis-config.xml文件中,可以通过以下配置进行MyBatis事务管理:<transactionManager type="JDBC/MANAGED" />
<transactionManager type="JDBC/MANAGED />则未开启事务。MyBatis提供了两种事务管理机制:
// 这个版本仅用于理解,实际使用见下一个版本
public static void main(String[] args){
// 因为sqlSession用于控制事务,声明在try中的话有作用域限制。
SqlSession sqlSession = null ;
try{
// 1.获取SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 2.获取SqlsessionFactory对象
InputStream s = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(s);
// 3.获取SqlSession对象,开启事务openSession
sqlSession = sqlSessionFactory.openSession();
// 4.执行sql,处理业务
int count = sqlSession.insert("insertTt");
System.out.println("插入几条记录:"+count);
// 5.关闭事务
sqlSession.commit();
} catch (Exception e){
// 出现异常时回滚事务
if (sqlSession != null){
sqlSession.rollback();
}
e.printStackTrace();
} finally {
if (sqlSession != null){
// 回滚事务后,关闭会话(释放资源)
sqlSession.close();
}
}
}#{}:本质是占位符,能用#{}就不要用${}。底层使用的JDBC的"?"占位符。因为一个数据库对应一个SqlSessionFactory对象,因而只创建一个SqlSessionFactory对象即可,不用每次都在方法中创建一个SqlSessionFactory对象。所以将其封装为工具类使用,不用每次都创建。
// SqlSessionUtil工具类
public class SqlSessionUtil {
// 工具类的构造方法一般都是私有化的
// 工具类中所有的方法都是静态的,直接使用类名调用,不需要new对象
// 为了防止new对象,构造方法私有化
private SqlSessionUtil(){}
private static SqlSessionFactory sqlSessionFactory;
// 类在加载时执行,且只执行一次
// SqlSessionUtil工具类在进行第一次加载的时候,解析mybatis-config.xml文件。创建SqlSessionFactory对象。
static {
try{
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// 获取会话对象
public static SqlSession openSession(){
return sqlSessionFactory.openSession();
}
}
// 在DAO中调用工具类
SqlSession sqlSession = SqlSessionUtil.openSession();
int count = sqlSession.insert("insertTt");
System.out.println(count);
sqlSession.commit();
sqlSession.close();开发中,我们插入数据时,不能在Java中把值写死,而是从前端获取,那么应该怎么做呢:
#{index}占位,index是Map的key。示例:
// Mapper
// null表示没有值,数据库也会是null
// 这里的index必须和Map中的key对应,否则数据库值为null。因为index匹配不到相应的值,相当于没有插入值。
<insert id="insertXxx">
insert into 表(字段1,字段2,字段3,字段4) values(null,#{k1},#{k2},#{k3});
</insert> // DAO
// 使用Map集合对前端传来的数据进行封装。
Map<String,Object> map = new HashMap();
map.put("k1","value1");
map.put("k2","value2");
map.put("k3","value3");
// 开发中Map的key为字段名。
// insert方法可以如两个参:sql的id,一个对象
// 对象是封装的数据,sql语句会从这个对象读取数据
int count = sqlSession.insert("insertXxx",map);
System.out.println(count);
sqlSession.commit();
sqlSession.close();步骤:
示例:
// Car实体类
public class Car {
// 数据类型用包装类型,防止null值。
private Long id;
private String carNum;
private Double brand;
...省略setter、getter、构造方法
}
// Mapper
<insert id="insertXxx">
insert into 表(字段1,字段2,字段3,字段4) values(null,#{carNum},#{brand});
</insert>
// DAO
public void xxx(){
Car car = new Car(null,'1001','奥迪')
}
int count = sqlSession.insert("insertXxx",car)
...
sqlSession.commit();
sqlSession.close();前面讲的方法都是传递一个参数,那么传递多个参数怎么传递呢?
如果传递多个参数mybatis怎么做的呢?
<select id="" resultType="">
select * from 表 where xxx=#{arg0} and xxx=#{arg1}
或
select # from 表 where xxx=#{param1} and xxx=#{param2}
</select>// 传递name,sex两个参数
List<XXX> 方法名(String name, Character sex);对于多个参数入参,前面那种方式可读性太差,可以使用注解入参。
使用@param注解命名参数:
// Mapper
<!-- id和注解方法名一致 -->
<select id="getUsers" resultType="映射类型">
select id, car_num
from 表
where id = #{id} and car_num = #{num} //值直接使用注解的值即可
</select>// DAO
public List<Car> getUsers(@Param("num") String carNum, @Param("brand") Double brand);// Mapper
<!-- 根据id查询一个,resultType指定将结果集封装成什么类型的对象(映射类型) -->
<!-- 全限定类名:就是类名全称,带包路径的用点隔开,例如: java.lang.String。
即全限定名 = 包名+类型 -->
<!-- 映射类型要更具返回值类型来指定,可以使用类型别名(见官网,如java.lang.Integer别名为int或integer)-->
<select id="selectById" resultType="全限定类名">
select * from 表 where id=#{id}
<!-- 当存在复合词时,通常数据库使用“-”链接,Java使用“小驼峰”,
因而需要用as给结果集取别名,否则mybatis在结果集中匹配不到相应字段的值,导致返回值为null。 -->
select id,car_name as carName,brand from 表 where id=#{id}
</select>
<!-- 查询所有,resultType指定将结果集封装成什么类型的对象 -->
<select id="selectAll" resultType="全限定类名">
select * from 表
select id,car_name as carName,brand from 表
</select>
<!-- 但是上面这种用别名的范式不够灵活,若有多条这样的语句,每次都要取别名,太麻烦 -->
<!-- 用sql片段 -->
<sql id="brand_column">
id,car_name as carName,brand
</sql>
<select id="selectAll" resultType="全限定类名">
select <include refid="brand_column" /> from 表
</select>
<!-- 上面这种方式也不够灵活,因为有的字段可能不需要,就要定义多个sql片段 -->
<!-- 使用resultMap,resultMap会自动根据查询的字段完成映射 -->
<!-- id:resultMap唯一标识;type:映射的类型,支持别名(将结果集封装成什么类型的对象)-->
<resultMap id="brandResultMap" type="全限定类名">
<!-- 给id取别名 -->
<id cloumn="表中的字段" property="实体类的属性" />
<!-- 给一般字段取别名 -->
<result column="表中的字段" property="实体类的属性" />
</resultMap>
将select中resultType换成resultMap
<select id="selectAll" resultMap ="resultMap的id">
select * from 表
select car_name from 表 // 不用再指定别名
</select>// Car实体类
public class Car {
// 数据类型用包装类型,防止null值。
private Long id;
private String carNum;
private Double brand;
...省略setter、getter、构造方法
}
/****************************************************************/
// DAO
// 查询一个
Object car = sqlSession.selectOne("selectById",id);
System.out.println(car);
sqlSession.close();
/****************************************************************/
// 查询所有
List<Object> cars = sqlSession.selectList("selectAll");
// 遍历
cars.forEach(cars->System.out.println(car));
sqlSession.close();在mybatis-config,xml文件中开启驼峰映射:
<!--放在properties后边-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>开启后,在mapper的sql标签的resultType中就可以直接写类名,mybatis会自动映射
<selece id="" resultType="类名"></select>select * from 表 where xxx like concat('%',#{xxx},'%'),使用MySQL的concat()函数拼接字符串select * from 表 where xxx like "%"#{xxx}"%",使用#{}拼接字符串根据id修改:值的传递可以根据增得方法使用Map或Bean来封装。
<update id="updateById">
update 表 set
car_num=#{carNum},
brand=#{brand}
where
id=#{id}
</update>// Car实体类
public class Car {
// 数据类型用包装类型,防止null值。
private Long id;
private String carNum;
private Double brand;
...省略setter、getter、构造方法
}
// DAO
Car car = new car(4L,"1001","50")
int count = salSession.update("updateById",car)
...
sqlSession.commit();
sqlSession.close();// Mapper
// 删除一个
<!-- 如若只有一个占位符,占位符里的值可以随便写,但最好见名知意 -->
<delete id="delById">delete from 表 where id = #{id}</delete>
// 批量删除1
<delete id="deleteParamsByIds">
<!--
collection表示参数的类型,可以选择array list;
open在语句开始位置添加内容,close在语句结束位置添加内容;
item当前元素,如果是array就表示array[i],如果是list就表示当前下标对象;
separator分隔符,一般选用半角,(逗号)
-->
delete from ts_sys_params where id in
<foreach collection="array" open="(" close=")" item="item" separator="," >
#{item}
</foreach>
</delete>
// 批量删除2
int deleteMore(@param("ids")String ids)
<delete id="deleteMore" >
<!-- 不能用#{},因为他会给每一个值添加单引号,如:'1','2','3' -->
delete from 表 where id in(${ids})
</delete>// DAO
int count = sqlSession.delete("delById",id)
...
sqlSession.commit();
sqlSession.close();在SQL优化中,我们会将数据拆分为多个表,查询的时候可以指定不同表名来查询,可以通过“${}”动态设置表的值。
List<Car> getAlluser(@param("tableName") Striing tableName);<select id="getAlluser" resultType="映射类型">
select * from ${tableName}
</select>有时候,我们在插入记录的时候需要获取插入记录的自增主键id的值,因为JDBC支持该功能,所以MyBatis也支持。
<!--
useGeneratedKeys:设置当前sql使用了自增主键,会返回主键值;
keyProperty:返回的主键映射到结果集的哪个属性上
-->
<insert id="sqlId" useGeneratedKeys="true" keyProperty="id">
insert into ....
</insert>开发中我们定义一个DAO接口,其实现类需要我们自己去写,太过麻烦。mybatis提供了SqlSession.getMapper方法为我们动态生成DAO接口的实现类,我们只需要写一个dao接口和抽象方法即可。
mybatis采用了代理模式,在内存中生成dao接口的代理类,然后创建代理类的实例对象。
使用getMapper的前提是:SqlMapper.xml文件中的namespace必须是dao接口的全限定名称,id必须是dao接口的方法名。
使用方法:
SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper(是接口).class);在mapper.xml文件的mapper标签有一个namespace属性,用来指定命名空间。防止sqlId重复。
namespace.id来指定sql语句,比如
List<Object>> cars = sqlSession.selectList("namespace.id")namespace.id也是指定sqlId的完整写法。if标签可以用于动态拼接sql语句。
<select id="" resultType="">
select * from 表 where
<!--
当test返回true,当前标签的sql语句才会被拼接,
test中写拼接当前sql的条件判断语句,
mybatis的动态sql不能使用“&&”,只能使用and。
-->
<if test="条件语句">sql语句</if>
<if test="条件语句">sql语句</if>
</select>where标签的作用:让where子句更加动态智能。
<select id="" resultType="">
select * from 表
<where>
<if test="条件语句">sql语句</if>
<if test="条件语句">sql语句</if>
</where>
</select><select id="" resultType="">
select * from 表
<!--
prefix="where":在第一个if子句前面加上where;
suffixOverrides="and|or":删除每个if子句末尾的and或or;
-->
<trim prefix="where" suffixOverrides="and|or">
<if test="条件语句">sql语句</if>
<if test="条件语句">sql语句</if>
</trim>
</select><update id="">
update 表
<set>
<if test="carNum != null and carNum != ''">car_num=#{carNum},</if>
<if test="brand !=null and brand != ''">brand=#{brand},</if>
</set>
where id = #{id}
</update><choose>
<!-- 执行test条件成立的那个sql子句,若都为false则执行otherwise子句 -->
<when test="条件">sql子句</when>
<when test="条件">sql子句</when>
<when test="条件">sql子句</when>
<otherwise>sql子句</otherwise>
</choose>delete from 表 where id in(1,2,3);delete from where id=1 or id=2 or id=3;collection:指定数组或集合,指只能是array或arg0或注解item:每次遍历的当前项名称separator:每一项之间的分隔符open:循环语句开始的内容close:循环语句结束的内容// 法1
<delete id="sqlId">
<!-- delete from 表 where id in(1,2,3) -->
delete from 表 where id in
<!-- collection表示参数的类型,可以选择array list;
open在语句开始位置添加内容,close在语句结束位置添加内容;
item当前元素,如果是array就表示array[i],如果是list就表示当前下标对象;
separator分隔符,一般选用半角,(逗号)
-->
<!-- 这里in后面的“()”在foreach中指定了 -->
<foreach collection="array" open="(" close=")" item="item" separator="," >
#{item}
</foreach>
</delete>
// 法2
<delete id="sqlId">
delete from 表 where
<foreach collection="array" iteam="iteam" separator="or">
id=#{iteam}
</foreach>
</delete>sql批量插入:
insert into 表 values(xxx,xxx,xxx),(xxx,xxx,xxx),(xxx,xxx,xxx)foreach批量插入:
<insert id="sqlId">
insert into 表 values
<foreach collection="数组|集合" iteam="iteam" separator=",">
(#{iteam.xxx},#{iteam.xxx},#{iteam.xxx},#{iteam.xxx})
</foreach>
</insert>// sql片段
<sql id="sqlId">
id,
car_num as carNum,
...
</sql>
<select id="sqlId" resultType="映射类型">
<!-- 使用sql片段 -->
select <include refid="sql片段id" />
from 表 where ...
</select><?xml version="1.0" encoding="UTF-8" ?>
<!--
这里的configuration就是根标签
.dtd规定了标签的排序、嵌套关系,对标签的排序和嵌套指定了一套规则
-->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--default表示默认使用的环境-->
<environments default="development">
<!--id指定使用的环境-->
<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/mybatisdemo"/>
<property name="username" value="root"/>
<property name="password" value="2525"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--从类路径中加载资源-->
<mapper resource="CarMapper.xml"/>
</mappers>
</configuration><!--default表示默认使用的环境-->
<environments default="development">
<!--id指定使用的环境-->
<!--在SqlSessionFactoryBuilder.build(ResourceAsStream("mybatis-config.xml"),环境id)可以指定环境id,若不指定则使用默认环境-->
<environment id="development">其他配置</environment>
</environments>在mybatis-config.xml文件中,可以通过以下配置进行MyBatis事务管理:<transactionManager type="JDBC/MANAGED" />
<transactionManager type="JDBC/MANAGED />则未开启事务。MyBatis提供了两种事务管理机制:
<transactionManager type="JDBC"/><!--
type:指定数据源的类型,即用什么方式类获取Connection对象。有三个值,分别是:
UNPOOLED:不使用数据库连接池技术,每次请求都会创建一个新的Connectiom对象。
POOLED:使用mybatis自己实现的数据库连接池。
JNDI:集成其他第三方数据库连接池。JNIT也是一套规范,是Java命名目录接口,Tomcat、Jetty、WebLogic、WebSphere这些服务器(容器)都实现了这个规范。
集成方式:https://blog.csdn.net/qq_45872039/article/details/128623005?spm=1001.2014.3001.5501
-->
<dataSource type="">
type的值不同,dataSource 内部的配置也不同,具体见官网
</dataSource>xxx.properties。在properties中配置的property,在其他地方可以通过这里的name来获取值,格式:${name},但也是在property标签使用。
<properties>
<property name="jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbc.url" value="jdbc:mysql://localhost:3306/mybatisdemo"/>
</properties>
<dataSource type="">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
</dataSource>xxx.properties。xxx.properties文件示例:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatisdemo在mybatis-config.xml文件中导入
// 导入文件
<properties resource="xxx.properties文件路径" />
// 然后就可以通过${jdbc.driver}读取配置文件
<property name="driver" value="${jdbc.driver}"/>在开发中,我们要获取分页的数据其实很简单,就直接写相应的sql语句就能拿到想要的数据。但是获取分页相关的数据就比较难。
分页相关的数据:


这些信息可以返给前端用于配置分页导航栏:什么时候显示上一页,导航栏显示哪些页码等信息

MySQL分页查询语法:
SELECT 字段 FROM 表名 LIMIT 偏移量,条目数量;SELECT 字段 FROM 表名 LIMIT 条目数量 OFFSEF 偏移量;(MySQL新特性8.0) (pageNum-1) * pageSize第一步:在pom.xml中引入PageHelper依赖,在maven坐标仓库查找。 第二步:在mybatis-config.xml文件中配置插件
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>第三步:在Mapper中写sql,但是分页部分的sql语句不用写,pagehelper插件会自动给我们加上。
<select id="" resultType="">
select * from 表 // limit LIMIT 偏移量,条目数量;这部分不用写
</select>第四步:
// 获取分页数据
PageHelper.stratPage(pageNum,pageSize);
List<Car> cars = mapper.selectAll();//cars 中包含了所有分页数据
// 获取分页数据,和额外的分页信息,acrPageInfo 包含了所有信息
PageInfo<Car> acrPageInfo = new PageInfo<>(cars,导航页码数)//导航页码数:前端导航卡片直接显示的页码数量前面讲解的都是一对一的映射关系,也就是一个类(一张表)对应一个实体(对象),但实际环境中是存在一对多、多对一、多对多的关系的。
存在多张表时,怎么区分主表和从表呢?
多对一需要查询两张表,其中多的一方是主表。
多表连接需要外键来实现。如下图,当我们根据sid在学生表中查询,可以拿到学生的cid进而可以在班级表中根据cid查询。
具体的类需要这样来设计,如下图,因为Student表是主表,一张表对应的是Java的一个类,所以可以在Student这个类中可以将Clazz这个类作为其成员属性。

// 学生类
public class Student{
private Clazz clazz;
}
// 班级类
public class Clazz{}
public interface StudentMapper{
Student selectById(Integer id);
}<mapper namespace="com.mapper.StudentMapper">
<resultMap id="studentResultMap" type="Student">
<!--column是结果集的字段,property是Java的属性,resultMap将column的值映射到property上-->
<id property="sid" column="sid" />
<result property="sname" column="sname" />
<result property="clazz.cid" column="cid" />
<result property="clazz.cname" column="cname" />
</resultMap>
</mapper>
<select id="selectById" resultMap="studentResultMap">
select
s.sid,s.sname,c.cid,c.cname //查询两表中的数据
from
t_stu s left join t_clazz c on s.cid=c.cid //s.cid=c.cid是多表查询连接条件
where
s.sid=#{sid}
</select>SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.selectById(具体id);
// student结果集中有所有查询的数据// 学生类
public class Student{
private Clazz clazz;
}
// 班级类
public class Clazz{}
/************************************************************************/
public interface StudentMapper{
Student selectById(Integer id);
}<mapper namespace="com.mapper.StudentMapper">
<resultMap id="studentResultMapAssociation" type="Student">
<!--column是结果集的字段,property是Java的属性,resultMap将column的值映射到property上-->
<id property="sid" column="sid" />
<result property="sname" column="sname" />
<!--
association翻译为关联,即一个Student对象关联一个Clazz对象
property:提供被映射的属性名
javaType:指定映射的Java类型
-->
<association property="clazz" javaType="Clazz">// Student中有clazz属性。
<id property="cid" column="cid" />
<result property="cname" column="cname" />
</association>
</resultMap>
</mapper>
<select id="selectById" resultMap="studentResultMap">
select
s.sid,s.sname,c.cid,c.cname //查询两表中的数据
from
t_stu s left join t_clazz c on s.cid=c.cid //s.cid=c.cid是多表查询连接条件
where
s.sid=#{sid}
</select>SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.selectById(具体id);
// student结果集中有所有查询的数据
第一步:查主表
<mapper namespace="com.mapper.StudentMapper">
<resultMap id="studentResultMapStep" type="Student">
<!--column是结果集的字段,property是Java的属性,resultMap将column的值映射到property上-->
<id property="sid" column="sid" />
<result property="sname" column="sname" />
<!--
association翻译为关联,即一个Student对象关联一个Clazz对象:
property:提供被映射的属性名;
select:指定第二条sql语句,namespace.sqlId;
column="cid"就是第二条语句需要的cid,从这里传过去
-->
<association
property="clazz"
select="com.mapper.ClazzMapper.selectByIdStep2"
column="cid"
>// Student中有clazz属性。
<id property="sid" column="sid" />
<result property="cname" column="cid" />
</association>
</resultMap>
</mapper>
<select id="selectByIdStep1" resultMap="studentResultMapStep">
select sid,sname,cid from t_stu where sid=#{sid}
</select>第二步:查从表
<mapper namespace="com.mapper.ClazzMapper">
<select id="selectByIdStep2" resultType="Clazz">
select cid,cname from t_clazz where cid = #{cid} // cid从第一步的association的column传过来
</select>
</mapper>懒加载好处:有的时候再执行,比如我们只需要查主表中的某个字段的时候,就不会执行从表的查询操作,提高了性能。
mtbatis-config.xml文件中配置<settings>
<setting name="lazyLoadingEnabled" value="true" />
</settings><association fetchType="eager">
<association fetchType="lazy">,通常不会用这种方式开启,大多情况下我们都是需要懒加载机制来提升性能的。
一对多中,一的一方是主表,如下图t_clazz是主表。
Java程序需要这样设计,设计两个类一个Student对于学生表,一个Clazz对应班级表。将从表对应的类作为主表的属性,类型为List<从表对应的类>类型的集合。因为这个属性有多个值,所以需要List集合来存储。

// 对应主表
public class Clazz{
prviate List<Student> stus;
}
/********************************************************************************/
// 对应从表
public class Student{}<mapper namespace="com.mapper.StudentMapper">
<resultMap id="clazzesultMap" type="Clazz">
<!--column是结果集的字段,property是Java的属性,resultMap将column的值映射到property上-->
<id property="cid" column="cid" />
<result property="cname" column="cname" />
<!--
collection翻译为集合
property:提供被映射的集合
ofType:指定集合中当前元素的类型
-->
<collection property="stus" ofType="Student">
<id property="sid" column="sid" />
<result property="sname" column="sname" />
</collection >
</resultMap>
</mapper>
<select id="selectByCollection" resultMap="clazzResultMap">
select
s.sid,s.sname,c.cid,c.cname //查询两表中的数据
from
t_clazz c left join t_stu s on c.cid=#{cid} //s.cid=c.cid是多表查询连接条件
where
s.sid=#{sid}
</select>// 第一步
<mapper namespace="com.mapper.ClazzMapper">
<ResultMap id="clazzResultStep" type="Clazz">
<id property="cid" column="cid" />
<result property="cname" column="cname" />
<!-- select的值是第二条sql语句id,namespace.sqlId-->
<collection
property="stus"
select="com.mapper.StudentMapper.selectByStep2"
column="cid" />
</ResultMap>
<select id="selectByStep1" resultType="clazzResultStep">
select cid,cname from t_clazz where cid =#{cid}
</select>
</mapper>
// 第二步
<mapper namespace="com.mapper.StudentMapper">
<select id="selectByStep2" resultType="Student">
select * from t_stu where cid=#{cid}
</select>
</mapper>将多对多拆分为一对多。
缓存原理:将DQL语句的查询结果集放到缓存(内存)中,当下一次执行这条select语句的时候,直接从缓存中取数据,步再查数据库。 清空一级缓存。清空一级缓存。<acahe />可序列化的,也就是必须实现java.Serializable接口关闭(close)或提交(commit)后,一级缓存中的数据才会被写到二级缓存中,此时二级缓存才可用。也就是说先从一级缓存获取数据,再从二级缓存获取数据。失效的情况: 清空(一级缓存也会被清空)










直接在MyBatis项目pom.xml文件中引入junit依赖即可。
在主程序中写程序代码,在test中new实例对象调用方法,传参,看实际值和期望值是否一致。
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>默认情况下MyBatis项目运行日志是没有开启的。
MyBatis常见的日志组件:
SLF4J:lockback实现了SLF4J规范。
LOG4J
LOG4J2
STDOUT_LOGGING:标准日志,MyBatis已经实现了这种日志,不需要引入jar包,但是日志信息不完整。开启方法:
<settings>
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>集成logback日志框架:
logback.xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<target>System.out</target>
<encoder>
<!-- 日志输出格式:%d表示日期时间,%-5level:日志级别 ,%c取类 %thread表示线程名(在哪个方法执行),
%msg:日志消息,%n是换行符 ,不会用就去百度一下logback的日志格式-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}[%-5level] %c [%thread] :%msg%n</pattern>
</encoder>
</appender>
<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->
<appender name="FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}-%msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<!-- 日志输出路径 -->
<file>D:/zhu/logback/data1.log</file>
<!-- 指定日志文件拆分和压缩规则 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 通过指定压缩文件的名称,来确定分割文件方式 -->
<fileNamePattern>D:/zhu/logback/data2-%d{yyyy-MMdd}.log%i.gz </fileNamePattern>
<!-- 文件拆分大小 -->
<maxFileSize>1MB</maxFileSize>
</rollingPolicy>
</appender>
<!-- level:用来设计打印级别,大小写无关:TRACE,DEBUG,INFO,WARN,ERROR,ALL和OFF,默认debug
<root>可以包含零个或多个<appender-ref>元素,标识这个输出位置将会被本日志级别控制。
-->
<root level="ALL">
<!-- 注意:如果这里不配置关联打印位置,该位置将不会记录日志 -->
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</configuration>