本文为SSM框架 【Mybatis】 相关知识,MyBatis 是一款优秀的半自动的ORM持久层框架,下边将对Mybatis的简介
、Mybatis的CRUD实现
,Mybatis的配置文件
,Mybatis的日志配置
,resultMap详解
,分页实现
,注解式开发
,Lombok的使用
,关联映射
,动态SQL
,Mybatis缓存
等进行详尽介绍~
🚩 Are you ready❓ Let’s Go ❗️
📌博主主页:小新要变强 的主页
👉Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~
👉算法刷题路线可参考:算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~
👉Java微服务开源项目可参考:企业级Java微服务开源项目(开源框架,用于学习、毕设、公司项目、私活等,减少开发工作,让您只关注业务!)
↩️本文上接:最全面的Mybatis教程,从“开局”到“通关”(一)
分页(Paging):即有很多数据,我们就需要分页来分割数据,可提高整体运行性能,增强用户使用体验需求等。
不使用分页将遇到的问题:
🍀语法
-- 语法:select * from xxx limit startIndex,pageSize
select * from user limit 3;
mybatis的sql语句如果有多个参数,需要用map封装。
🍀Mapper接口
List<User> selectLimit(Map<String,Integer> map);
🍀xxxMapper.xml
<select id="selectLimit" parameterType="map" resultMap="UserMap">
select * from mybatis.user limit #{startIndex},#{pageSize}
select>
🍀测试
Test.java:
public class UserDaoTest {
@Test
public void limitTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> list=mapper.selectLimit(map);
for (User u:
list) {
System.out.println(u);
}
sqlSession.close();
}
}
这种方法官方不推荐。
🍀Mapper接口
List<User> selectRowBounds();
🍀xxxMapper.xml
<select id="selectRowBounds" resultMap="UserMap">
select * from mybatis.user
select>
🍀测试
@Test
public void selectRowBounds(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
RowBounds rowBounds = new RowBounds(0,2);
List<User> list = sqlSession.selectList("com.wang.dao.UserMapper.selectRowBounds"
,null,rowBounds);
for (User user : list) {
System.out.println(user);
}
sqlSession.close();
}
🍀Mapper接口
@Select("select * from mybatis.user")
List<User> selectAll();
🍀注册绑定
mybatis-config.xml:
<mappers>
<mapper class="com.wang.dao.UserMapper"/>
mappers>
🍀测试
@Test
public void selectAll(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//底层主要应用反射
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> list=mapper.selectAll();
for (User user : list) {
System.out.println(user);
}
sqlSession.close();
}
🍀设置自动提交
MybatisUtils.java:
public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(true); }
🍀Mapper接口
//多个参数情况下,有两种解决方式,一个map封装,另一种是注解Param
@Select("select * from mybatis.user where id=#{id}")
User selectUserById(@Param("id") int id);
@Select("select * from mybatis.user")
List<User> selectAll();
@Insert("insert into mybatis.user() values(#{id},#{name},#{password}) ")
boolean insertUser(User u);
@Update("update user set name=#{name},pwd=#{password} where id = #{id}")
boolean updateUser(User u);
@Delete("delete from mybatis.user where id=#{id}")
boolean deleteUser(@Param("id") int id);
🍀测试
@Test
public void selectAll(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//底层主要应用反射
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// List list=mapper.selectAll();
// for (User user : list) {
// System.out.println(user);
// }
/**
User u=mapper.selectUserById(1);
System.out.println(u);
*/
// boolean isInserted=mapper.insertUser(new User(4,"图拉真","dgsdgs"));
// if (mapper.updateUser(new User(6,"寒江雪",null)))
if (mapper.deleteUser(6))
for (User user : mapper.selectAll()) {
System.out.println(user);
}
sqlSession.close();
}
这个注解是为SQL语句中参数赋值而服务的。@Param的作用就是给参数命名,比如在mapper里面某方法A(int id),当添加注解后A(@Param(“userId”) int id),也就是说外部想要取出传入的id值,只需要取它的参数名userId就可以了。将参数值传如SQL语句中,通过#{userId}进行取值给SQL的参数赋值。
实例一:@Param注解基本类型的参数
🍀Mapper接口
public User selectUser(@Param("userName") String name,@Param("password") String pwd);
🍀xxxMapper.xml
<select id="selectUser" resultMap="User">
select * from user where user_name = #{userName} and user_password=#{password}
select>
其中where user_name = #{userName} and user_password = #{password}中的userName和password都是从注解@Param()里面取出来的,取出来的值就是方法中形式参数 String name 和 String pwd的值。
实例二:@Param注解JavaBean对象
SQL语句通过@Param注解中的别名把对象中的属性取出来然后复制
🍀Mapper接口
public List<User> getAllUser(@Param("user") User u);
🍀xxxMapper.xml
<select id="getAllUser" parameterType="com.vo.User" resultMap="userMapper">
select
from user t where 1=1
and t.user_name = #{user.userName}
and t.user_age = #{user.userAge}
select>
注意点:
Lombok是一个可以通过简单的注解形式来帮助我们简化消除一些必须有但显得很臃肿的Java代码的工具,通过使用对应的注解,可以在编译源码的时候生成对应的方法。
🍀(1)IDEA左上角File->Settings->Plugins
🍀(2)搜索Lombok,下载安装
🍀(3)导入maven
pom.xml:
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.10version>
dependency>
Lombok的支持:
@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val
@var
experimental @var
@UtilityClass
Lombok config system
Code inspections
Refactoring actions (lombok and delombok)
常用支持:
使用方法:
在具体的实体类上加相应的注解。
以多个学生对应一个老师为例,存在:
create table `teacher`(
`id` int not null,
`name` varchar(30) default null,
primary key(`id`)
) engine=InnoDB default charset=utf8;
insert into teacher values (1,'王老师');
create table `student`(
`id` int not null,
`name` varchar(30) default null,
`tid` int not null,
primary key(`id`),
key `FK_tid` (`tid`),
constraint `FK_tid` foreign key(`tid`) references `teacher`(`id`)
) engine=InnoDB default charset=utf8;
🍀(1)导入Lombok
🍀(2)新建Teacher,Student实体类
🍀(3)新建Mapper接口
🍀(4)在resources新建com->xxx->dao文件夹
🍀(5)新建xxxMapper.xml文件
🍀(6)在mybatis-config.xml中注册绑定xxxMapper.xml
🍀(7)在TeacherMapper接口中创建selectAll()方法
🍀(8)在TeacherMapper.xml中写对应的查询
🍀(9)新建测试类,在测试类中测试使用
🍀实体类
Student.java:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
private Teacher teacher;
}
Teacher.java:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
}
🍀Mapper接口
List<Student> selectAll();
🍀xxxMapper.xml
<resultMap id="student_teacher" type="Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
resultMap>
<select id="selectAll" resultMap="student_teacher">
select * from mybatis.student
select>
<select id="getTeacher" resultType="Teacher">
select * from mybatis.teacher where id=#{tid}
select>
🍀测试
@Test
public void selectAll(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.selectAll();
for (Student s:
studentList) {
System.out.println(s);
}
sqlSession.close();
}
🍀Mapper接口
List<Student> selectAll2();
🍀xxxMapper.xml
<select id="selectAll2" resultMap="S_T">
select s.id sid,s.name sname,t.name tname
from mybatis.student s,mybatis.teacher t
where s.tid=t.id
select>
<resultMap id="S_T" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
association>
resultMap>
🍀测试
@Test
public void selectAll(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.selectAll();
for (Student s:
studentList) {
System.out.println(s);
}
sqlSession.close();
}
🍀实体类
Student.java:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
private int tid;
}
Teacher.java:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
//老师拥有多个学生
private List<Student> students;
}
🍀Mapper接口
public interface TeacherMapper {
List<Teacher> selectAll();
//获取指定老师下的所有学生
Teacher getTeacher(@Param("tid")int id);
Teacher getTeacher2(@Param("tid")int id);
List<Student> getStudents(@Param("tid")int id);
}
🍀xxxMapper.xml
<select id="selectAll" resultType="Teacher">
select * from mybatis.teacher
select>
<select id="getTeacher" resultMap="S_T">
select t.id tid, t.name tname,s.name sname
from mybatis.teacher t,mybatis.student s
where s.tid=tid and tid=#{tid}
select>
<resultMap id="S_T" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
collection>
resultMap>
<select id="getTeacher2" resultMap="student_teacher">
select * from mybatis.teacher where id=#{tid}
select>
<resultMap id="student_teacher" type="Teacher">
<result property="id" column="id"/>
<result property="name" column="name"/>
<collection property="students" column="id" ofType="Student" select="getStudents"/>
resultMap>
<select id="getStudents" resultType="Student">
select * from mybatis.student where tid=#{tid}
select>
🍀测试
@Test
public void selectAll(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.selectAll();
for (Student s:
studentList) {
System.out.println(s);
}
sqlSession.close();
}
MyBatis提供了对SQL语句动态的组装能力,大量的判断都可以在 MyBatis的映射XML文件里面配置,以达到许多我们需要大量代码才能实现的功能,大大减少了我们编写代码的工作量。
动态SQL的元素:
元素 | 作用 | 备注 |
---|---|---|
if | 判断语句 | 单条件分支判断 |
choose、when、otherwise | 相当于Java中的 case when语句 | 多条件分支判断 |
trim、where、set | 辅助元素 | 用于处理一些SQL拼装问题 |
foreach | 循环语句 | 在in语句等列举条件常用 |
if元素相当于Java中的if语句,它常常与test属性联合使用。现在我们要根据name去查找学生,但是name是可选的,如下所示:
<select id="findUserById" resultType="com.wang.entity.User">
select id,username,password from user
where 1 =1
<if test="id != null">
AND id = #{id}
if>
<if test="username != null and username != ''">
AND username = #{username}
if>
<if test="password != null and password != ''">
AND password = #{password}
if>
select>
上面的select语句我们加了一个1=1
的绝对true的语句,目的是为了防止语句错误,变成SELECT * FROM student WHERE
这样where后没有内容的错误语句。这样会有点奇怪,此时可以使用 < where>
元素。
<select id="findUserById" resultType="com.wang.entity.User">
select id,username,password from user
<where>
<if test="id != null">
AND id = #{id}
if>
<if test="username != null and username != ''">
AND username = #{username}
if>
<if test="password != null and password != ''">
AND password = #{password}
if>
where>
select>
有时候我们要去掉一些特殊的SQL语法,比如常见的and、or,此时可以使用trim元素。trim元素意味着我们需要去掉一些特殊的字符串,prefix代表的是语句的前缀,而prefixOverrides代表的是你需要去掉的那种字符串,suffix表示语句的后缀,suffixOverrides代表去掉的后缀字符串。
<select id="select" resultType="com.wang.entity.User">
SELECT * FROM user
<trim prefix="WHERE" prefixOverrides="AND">
<if test="username != null and username != ''">
AND username LIKE concat('%', #{username}, '%')
if>
<if test="id != null">
AND id = #{id}
if>
trim>
select>
有些时候我们还需要多种条件的选择,在Java中我们可以使用switch、case、default语句,而在映射器的动态语句中可以使用choose、when、otherwise元素。
<select id="select" resultType="com.wang.entity.User">
SELECT * FROM user
WHERE 1=1
<choose>
<when test="name != null and name != ''">
AND username LIKE concat('%', #{username}, '%')
when>
<when test="id != null">
AND id = #{id}
when>
choose>
select>
在update语句中,如果我们只想更新某几个字段的值,这个时候可以使用set元素配合if元素来完成。注意: set元素遇到,会自动把,去掉。
<update id="update">
UPDATE user
<set>
<if test="username != null and username != ''">
username = #{username},
if>
<if test="password != null and password != ''">
password = #{password}
if>
set>
WHERE id = #{id}
update>
foreach元素是一个循环语句,它的作用是遍历集合,可以支持数组、List、Set接口。
<select id="select" resultType="com.wang.entity.User">
SELECT * FROM user
WHERE id IN
<foreach collection="ids" open="(" close=")" separator="," item="id">
#{id}
foreach>
select>
<insert id="batchInsert" parameterType="list">
insert into `user`( user_name, pass)
values
<foreach collection="users" item="user" separator=",">
(#{user.username}, #{user.password})
foreach>
insert>
有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。
提取SQL片段:
<sql id="if-title-author">
<if test="title != null">
title = #{title}
if>
<if test="author != null">
and author = #{author}
if>
sql>
引用SQL片段:
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog
<where>
<include refid="if-title-author">include>
where>
select>
Mybatis系统默认定义了两级缓存:
🍀一级缓存是sqlsession级别的缓存
🍀一级缓存工作原理
一级缓存工作原理图解:
🍀一级缓存测试步骤
🍀一级缓存演示
Mapper接口:
User getUserById(int id);
xxxMapper.xml:
<select id="getUserById" parameterType="int" resultType="User">
select * from mybatis.user where id=#{id}
select>
Test.java:
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User u=mapper.getUserById(1);
System.out.println(u);
System.out.println("=============");
User user=mapper.getUserById(1);
System.out.println(user);
System.out.println(u==user);
sqlSession.close();
}
🍀缓存失效的情况
🍀一级缓存生命周期
🍀二级缓存是mapper级别的缓存
🍀二级缓存实现原理
<settings>
<setting name="cacheEnabled" value="true"/>
settings>
<cache eviction="LRU" flushInterval="100000"/>
🍀禁用二级缓存
在statement中可以设置useCache=false,禁用当前select语句的二级缓存,默认情况为true
<select id="getStudentById" parameterType="java.lang.Integer" resultType="Student" useCache="false">
在实际开发中,针对每次查询都需要最新的数据sql,要设置为useCache=“false” ,禁用二级缓存。
🍀flushCache标签:刷新缓存(清空缓存)
<select id="getStudentById" parameterType="java.lang.Integer" resultType="Student" flushCache="true">
一般下执行完commit操作都需要刷新缓存,flushCache="true 表示刷新缓存,可以避免脏读。
🍀二级缓存应用场景
🍀二级缓存局限性
MyBatis二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用MyBatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为MyBatis的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存。
🍀使用二级缓存
(1)开启全局缓存
<setting name="cacheEnabled" value="true"/>
(2)在要使用二级缓存的Mapper.xml中,写标签
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
(3)测试
@Test
public void getUserById2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
SqlSession sqlSession2 = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User u=mapper.getUserById(1);
System.out.println(u);
sqlSession.close();
System.out.println("============");
User user = mapper2.getUserById(1);
System.out.println(user==u);
sqlSession2.close();
}
(2)问题
我们需要实体类序列化,否则会抛出异常
(4)总结
🍀EhCache简介
EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认CacheProvider。Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。
🍀EhCache使用
(1)导包
<dependency>
<groupId>org.mybatis.cachesgroupId>
<artifactId>mybatis-ehcacheartifactId>
<version>1.2.2version>
dependency>
(2)写入配置文件(resources->ehcache.xml)
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<diskStore path="java.io.tmpdir/ehcache"/>
<defaultCache
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxEntriesLocalDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
defaultCache>
<cache name="HelloWorldCache"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="5"
timeToLiveSeconds="5"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"/>
ehcache>
(3)在Mapper中指定
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
(4)测试
@Test
public void getUserById2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
SqlSession sqlSession2 = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User u=mapper.getUserById(1);
System.out.println(u);
sqlSession.close();
System.out.println("============");
User user = mapper2.getUserById(1);
System.out.println(user==u);
sqlSession2.close();
}
🍀自定义缓存
只要实现了org.apache.ibatis.cache.Cache接口,就能定义自己的缓存,但是实现比较复杂,只需要会使用就行,ehcache是继承了AbstractEhcacheCache,该类已经实现了Cache接口。
public class MyCache implements Cache {
@Override
public String getId() {
return null;
}
@Override
public void putObject(Object key, Object value) {
}
@Override
public Object getObject(Object key) {
return null;
}
@Override
public Object removeObject(Object key) {
return null;
}
@Override
public void clear() {
}
@Override
public int getSize() {
return 0;
}
}
🍀实际开发中使用的缓存
在实际开发中,我们更多的使用Redis来做缓存。
👉Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~
👉算法刷题路线可参考:算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~
👉Java微服务开源项目可参考:企业级Java微服务开源项目(开源框架,用于学习、毕设、公司项目、私活等,减少开发工作,让您只关注业务!)