目录
在resources中写入一个mybatis-config.xml文件
设置自己mybatis库:
插件
pom.xml:
基础插件(必)
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
分页:
https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
实体类基础构造
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
实体类中的使用
@Date //get、set方法
@NoArgsConstructor //无参
@AllArgsConstructor //全部属性有参方法
数据库
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
单元测试(必)
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
整体:
mybatis-config.xml引入(规定引入的前后)
创建的位置:
<?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>
<!-- 分页-->
<plugins>
<!--设置分页插件-->
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
数据库
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/1010_db?serverTimezone=Asia/Shanghai&useSSL=false&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
引入
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<!--name是固定的 value根据需要配置为自己的内容-->
<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>
<!-- <mapper resource="com/java009/mapper/OrderMapper.xml"/>-->
<package name="com.java009.mapper"/>
</mappers>
配置文件执行流程
添加修改删除跟查询:
引包-pom.xml
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!-- //构造-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
</dependencies>
创建实体类
com.zz.entity
//添加注释
@Data
//没有构造方法
@NoArgsConstructor
//有构造方法
@AllArgsConstructor
创建mapper包--写实体类接口
写入业务层查询,修改,插入,删除
//查询
List<Student> listAll();
//插入
int insert (Student student);
//删除
int delete(int id);
//修改
int update(Student student);
对应的resources中创建跟实体类接口一样的路径跟名字
sql语句:
<!-- 查-->
<select id="listAll" resultType="com.zz.entity.Student">
select *from t_student;
</select>
<!-- 插-->
<insert id="insert" parameterType="com.zz.entity.Student">
insert into t_student
values (null,#{name},#{phone},#{email},#{profession},#{age});
</insert>
<!-- //改-->
<update id="update" >
update t_student set name=#{name},phone=#{phone},email=#{email},profession=#{profession},age=#{age} where id=#{id}
</update>
<!--删-->
<delete id="delete" >
delete from t_student where id=#{id}
</delete>
在resources中写入一个mybatis-config.xml文件
直接应用
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<!--name是固定的 value根据需要配置为自己的内容-->
<property name="driver" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/00520_db" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
测试
总节
//加载mybatis主配置文件
InputStream is= Resources.getResourceAsStream("mybatis-config.xml");
//创建SqlSessionFactory对
SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(is);
//通过会话工厂,创建sql会话对象
SqlSession session=factory.openSession();
//通过session获取mapper对象
StudentMapper studentMapper=session.getMapper(StudentMapper.class);
插
//通过mapper接口对象完成插入
Student s=new Student(null,"帅","123","309QQ.com","软件技术",13);
//插入
int add=studentMapper.insert(s);
System.out.println("插入记录"+add);
//提交代码
session.commit();
//关闭会话
查
//b.通过接口对象进行查询
List<Student> students = studentMapper.listAll();
System.out.println(students);
//5.关闭会话
session.close();
修改
Student student=new Student(25,"买天奇","15","150@qq.com","体育",30);
//调用方法
studentMapper.update(student);
//提交
session.commit();
//关闭会话
session.close();
删除
int add=studentMapper.delete(24);
session.commit();
session.close();
mybatis-connfig.xml的一些配置
- 关联配置文件-->
<properties resource="db.properties"></properties>
<!-- mybatis全文配置-->
<settings>
<!-- 配置日志文件-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- 配置数据库属性下划线变小驼峰-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 配置类型别名-->
<typeAliases>
<!-- 配置单个实体类的别名 -->
<!-- <typeAlias type="com.java009.entity.Emp" alias="emp"/> -->
<!-- 使用包配置的方式,批量配置该包下的所有实体类别名 -->
<package name="com.java009.entity"/>
</typeAliases>
映射文件
<mappers>
<!-- 关联mapper文件,获取针对表的增删改查配置-->
<!-- <mapper resource="com/java009/mapper/EmpMapper.xml"/>-->
<!-- 通过配置Mapper接口类,与Mapper文件进行映射-->
<!-- <mapper class="com.java009.mapper.EmpMapper"/>-->
<!-- 配置mapper接口包,会自动关联映射包中的所有类-->
<package name="com.java009.mapper"/>
</mappers>
特殊字符
<![CDATA[SQL语句]]
日志:
引入jar包:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
2.引入日志属性文件
名称必须为log4j.properties
log4j.rootLogger=DEBUG, Console
#Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
log4j.logger.java.sql.ResultSet=INFO
log4j.logger.org.apache=INFO
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
模糊查询
其中有concat(‘&’,key,‘&’)拼接查询
添加时获取递增的主键值
useGenerateKeys:是否使用生成的主键
KeyPoperty:生成后的主键赋给哪个属性
起别名·
单个实体类别名
<typeAliases>
<typeAlias type="com.zz.entity.Role" alias="R">
多个实体类
<package name="com.zz.entity"/>
对于多参传递方法(@param
如果列名跟属性名不一致的情况
方法1:
在查询时,进行重命名
方法2:使用resultMap方法重写属性名称
起别名
id:标识
type:执行完查询后返回的数据类型
-->
<resultMap id="myMap" type="com.zz.entity.Role">
<!-- id:主键标志 proper="属性
column:"列名"
-->
<id property="id" column="id"></id>
<result property="name" column="role_name"></result>
<result property="code" column="role_code"></result>
<result property="description" column="description"></result>
</resultMap>
方法3:
全文配置中设置下划线变为小驼峰
<setting name="mapUnderscoreToCamelCase" value="true"/>
一对一
sql语句
autoMapping:自动映射属性名称,将查询结果映射到Java中
<resultMap id="stuMap" type="student" autoMapping="true">
<id property="studentId" column="studentId"/>
<result property="studentName" column="student_name"/>
<result property="sex" column="sex"/>
<!-- 使用association 进行一对一对象关联映射-->
<association property="classInfo" javaType="ClassInfo">
<result property="className" column="class_name"/>
</association>
</resultMap>
一对多
//一对多关联学生对象
List<Student> studentList;
测试
public void Sq()throws Exception{
ClassInfoMapper mapper=session.getMapper(ClassInfoMapper.class);
List<ClassInfo> c=mapper.listAll();
for(ClassInfo classInfo:c){
System.out.println(classInfo);
}
}
include使用:
因为sql语句的重复性,用<sql id="">标签提取应用
动态标签
if
where
<!-- 多表关联查询-->
<select id="list" parameterType="booksQuery" resultMap="booksMap">
<include refid="mainQuery"></include>
-- 条件查询
<where>
<if test="bookName!=null and bookName!=''">
and book_name like concat('%',#{bookName},'%')
</if>
<if test="author!=null and author!=''">
and auther like concat('%',#{author},'%')
</if>
<if test="price">
and price=#{price}
</if>
<if test="pubDateStart!=null">
and pub_date >=#{pubDateStart}
</if>
<if test="pubDateEnd!=null">
<![CDATA[and pub_date<=#{pubDateEnd}]]>
</if>
<if test="category!=null and category.categoryName!=null and category.categoryName!=''">
and category_name=#{category.categoryName}
</if>
</where>
</select>
forach
根据id查询、插入
<select id="listByLds" resultMap="booksMap">
<include refid="mainQuery"></include>
where book_id in
<foreach collection="list" item="date" open="(" close=")" separator=",">
#{data}
</foreach>
</select>
<!-- 插入-->
<insert id="insertBath">
insert into tb_books
(book_name,author,price,pub_date,category_id)
VALUES
<foreach collection="list" item="book" separator=",">
(#{book.bookName},#{book.author},#{book.price},#{book.pubDate},#{book.category.categoryId})
</foreach>
</insert>
choose when
<select id="listByChoose" resultMap="booksMap">
<include refid="mainQuery"></include>
<!--<trim prefix="where" prefixOverrides="and">-->
<where>
<choose>
<when test="bookName!=null and bookName!=''">
and book_name like concat("%",#{bookName},"%")
</when>
<when test="author!=null and author!=''">
and author =#{author}
</when>
<otherwise>
and book_id = 1
</otherwise>
</choose>
</where>
<!---- </trim>-->
</select>
trim
set
一级缓存
缓存是存放数据的地方,即将产生的数据暂时存放到内存中。(缓存就是数据交换的缓冲区(称作Cache),是存贮数据(使用频繁的数据)的临时地方。当用户查询数据,首先在缓存中寻找,如果找到了则直接执行。如果找不到,则去数据库中查找。-大佬的解释)
-
应用场景(一级&含义)
即通过同一个SqlSession查询出来的数据会被缓存,第二次查询相同的数据的时候会直接从缓存中取数据,不会再去访问数据库再获取数据。
开启条件(一级)
Mybatis的一级缓存是默认开启的。
失效条件(一级)
使用不同的SqlSession 两次查询间存在对数据的增删改操作 两次查询的查询条件不一致 手动清空了缓存
一级缓存失效代码示例(后续二级缓存所使用的数据表一致)
下面以查询员工信息为例,即通过员工的id信息查询员工所有的信息(我们通过log4j实现日志功能,来查看查询语句执行的次数,即为访问数据库的次数,需要引入log4j的依赖和文件,可以直接查到)
员工表如下:
mapper.xml如下:
我们先看看SqlSession未失效时,Sql语句的执行情况(使用同一个SqlSession获取Mapper代理对象执行方法,查询员工信息
/**
* 测试一级缓存未失效的情况
*/
@Test
public void CacheTestAtFirst(){
//获取SqlSession对象(这是封装好了的方法,直接获取)
SqlSession sqlSession1 = SqlSessionUtil.getSqlSession();
//获取Mapper代理对象
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
CacheMapper mapper2 = sqlSession1.getMapper(CacheMapper.class);
//第一次查询员工信息
List<EmpMapper> empById1 = mapper1.getEmpById(1);
System.out.println(empById1);
//第二次查询员工信息
List<EmpMapper> empById2 = mapper2.getEmpById(1);
System.out.println(empById2);
//关闭资源
sqlSession1.close();
运行结果如下:
结论:我们通过日志可以看到,我们调用了两次查询员工信息的方法,但日志显示只执行了一次Sql语句,即只访问了一次数据库,因为第二次直接从缓存里拿数据了,这就是Mybatis的一级缓存,下面我们再看看一级缓存失效的情况。
以下为“使用不同的SqlSession".eg(测试类)
测试代码如下:
/**
* 测试一级缓存,Sqlsession级别-使用不同的SqlSession会导致缓存失效
*/
@Test
public void CacheTestByOne(){
//获取两个SqlSession对象
SqlSession sqlSession1 = SqlSessionUtil.getSqlSession();
SqlSession sqlSession2 = SqlSessionUtil.getSqlSession();
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
//执行Sql
List<EmpMapper> empById = mapper1.getEmpById(1);
System.out.println(empById);
CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
//执行Sql
List<EmpMapper> empById1 = mapper2.getEmpById(1);
System.out.println(empById1);
//关闭资源
sqlSession2.close();
sqlSession1.close();
运行结果如下:
结论:可以看到,使用不同的SqlSession对象后,日志显示了两次Sql查询语句,即访问了两次数据库,Mybatis的一级缓存失效了。
以下为“手动清空了缓存”.eg
测试代码示例如下:
/**
- 手动清空缓存使得一级缓存失效
*/
@Test
public void CacheTestByOneA(){
SqlSession sqlSession1 = SqlSessionUtil.getSqlSession();
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
List<EmpMapper> empById = mapper1.getEmpById(1);
System.out.println(empById);
//清空缓存
sqlSession1.clearCache();
CacheMapper mapper2 = sqlSession1.getMapper(CacheMapper.class);
List<EmpMapper> empById1 = mapper2.getEmpById(1);
System.out.println(empById1);
sqlSession1.close();
运行结果如下:
结论:与第一种失效的情况一致,还有两种条件也会造成Mybatis的一级缓存失效,测试过程类似, 不再一一展示。
二级缓存
应用场景(二级&含义)
和一级缓存的定义相似,即由同一个SqlSessionFactory创建的(不同)SqlSession对象查询的结果会被缓存。
开启条件(二级)
1.在mapper.xml文件中设置<settings>标签,默认是开启的,不需要设置
<settings> <setting name="cacheEnabled" value="true"/> settings>
2.在mapper.xml文件设置<cache/>标签
3.查询数据所转换的实体类类型必须转换实现序列化接口
4.必须在SqlSession关闭或提交后,才会开启二级缓存
二级缓存开启成功示例:
测试代码如下:
@Test
public void CacheTestByTwo() throws IOException {
//1.获取核心配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//2.获取Builder,从同一个sqlSessionFactory中获取SqlSession对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
List<EmpMapper> emp1 = mapper1.getEmpById(1);
System.out.println(emp1);
//关闭SqlSession
sqlSession1.close();
List<EmpMapper> emp2 = mapper2.getEmpById(1);
System.out.println(emp2);
sqlSession2.close();
sqlSession1.close();
}
运行结果如下:
结论:可以看到日志中只执行了一次Sql语句,即两次查询中,只访问了一次数据库(注意Mybatis的缓存查询顺序,先二级再一级,其中显示的命中率Hit Ratio也意味开启了二级缓存)
失效条件(二级)
两次查询之间执行了一次增删改操作SqlSession未关闭,数据被保存在一级缓存中
整合第三方缓存EHCach
即使用第三方缓存来代替Mybatis的二级缓存,这里不详细介绍
四.Mybatis一二级缓存开启顺序
查询数据时,会按照以下顺序获取数据
二级缓存中是否有数据一级缓存中是否有数据查询数据库
理解:Mybatis的二级缓存要比一级缓存作用域大,但也存在一级缓存中有的数据,二级缓存中没有,如使用不同的SqlSession查询数据的时候,会把查询的数据放到一级缓存中(二级缓存中没有)。
分页
插件:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.4</version>
</dependency>
</dependencies>
mybatis-config.xml
<plugins>
<!--设置分页插件-->
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
设置分页参数,当前页面,每页记录数
PageHelper.startPage(1,3);
使用查询结果
PageInfo<Student>pi=new PageInfo<>(studentList);
/分页导航的第一页
System.out.println("navigateFirstPage:分页导航的第一页"+pi.getNavigateFirstPage());
//最后一页
System.out.println("navigateLastPage:分页导航的最后一页"+pi.getNavigateLastPage());
//总页数
System.out.println("navigatePages:分页导航的总页数"+pi.getPages());
//当前页
System.out.println("pageNum:当前页"+pi.getPageNum());
//当前的上一页
System.out.println("prePage:当前上一页"+pi.getPages());
//当前下页
System.out.println("nextPage"+pi.getNextPage());
//每条的数据条数
System.out.println("pageSize:每页的数据条数"+pi.getPageSize());
//当前页的开始型号
System.out.println("startRow:当前页的开始型号"+pi.getStartRow());
//结束型号
System.out.println("endRow:结束型号"+pi.getEndRow());