项目收获总结--MyBatis的知识收获

news2024/11/15 8:14:03
一、概述

最近几天公司项目开发上线完成,做个收获总结吧~ 今天记录MyBatis的收获和提升。
在这里插入图片描述

二、获取自动生成的(主)键值

insert 方法总是返回一个 int 值 ,这个值代表的是插入的行数。若表的主键id采用自增长策略,自动生成的键值在 insert 方法执行完后可以被设置到传入的参数对象中。


<insert id=”insertUser” usegeneratedkeys=”true” keyproperty= id”>
    insert into table_name (name, age) values (#{name}, #{age})
</insert>
User user = new User();
user.setName(“fred”);
user.setAge(18);
int rows = mapper.insertUser(user);
// 完成后,id 已经被设置到user对象中
system.out.println(“插入数据条数:” + rows);
system.out.println(“插入数据的主键为:” + user.getid());
三、将sql执行结果封装为目标返回对象的方式和原理

方式:

第一种是使用 <resultMap> 标签,逐一定义数据库列名和对象属性名之间的映射关系。
第二种是使用 sql 列的别名功能(as 关键字),将列的别名书写为对象属性名。

原理:

使用列名与属性名的映射关系,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
四、延迟加载实现原理

Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association 指的就是一对一,collection 指的就是一对多查询。在 Mybatis 配置文件中,可以配置是否启用延迟加载 lazyLoadingEnabled=true|false。

原理是使用 CGLIB 创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用 a.getB().getName(),拦截器 invoke()方法发现 a.getB()是 null 值,那么就会单独发送事先保存好的查询关联 B 对象的 sql,把 B 查询上来, 然后调用 a.setB(b),于是 a 的对象 b 属性就有值了,接着完成 a.getB().getName() 方法的调用。

不光是 Mybatis,几乎所有的ORM框架,包括 Hibernate,支持延迟加载的原理都是一样的。

五、批量插入

第一种:采用SqlSession批量插入模式

List <User> users = new ArrayList();
// 注意这里 executortype.batch
SqlSession sqlsession = SqlSessionFactory.openSession(executortype.Batch);
try {
    UserMapper mapper = Sqlsession.getMapper(UserMapper.Class);
for (User user: users) {
    mapper.insertUser(user);
}
sqlsession.commit();
} catch (Exception e) {
    e.printStackTrace();
    sqlSession.rollback();
    throw e;
}
finally {
    sqlsession.close();
}

sql:

<insert id=”insertUser”>
    insert into userTable (name, age) values (#{name}, #{age})
</insert>

第二种:标签在XML映射文件中构建批量插入的SQL语句

<insert id="insertBatch">
    INSERT INTO userTable (column1, column2, ...)
    VALUES
    <foreach collection="list" item="item" index="index" separator=",">
        (#{item.field1}, #{item.field2}, ...)
    </foreach>
</insert>

需要传入一个实体类的List集合,column1, column2, … 是表中的列名,field1, field2, … 是你的实体类中的属性名。若数据集特别大,可能需要考虑数据库事务的隔离级别、批次大小、以及可能出现的内存溢出等问题。

六、自带分页与分页插件原理

Mybatis 本身使用 RowBounds 对象进行分页,针对 ResultSet 结果集执行的内存分页,而非物理分页。
但是在sql 直接用limit等关键字可以物理分页,也可使用分页插件来完成物理分页。
分页插件的基本原理是使用 Mybatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 SQL,然后重写 SQL,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。

七、Mapper(Dao)接口与XML映射文件关系

Mapper(Dao)接口的全限名,就是xml映射文件中的 namespace 的值,接口的方法名,就是映射文件中 Mapper 的 Statement 的 id 值;接口方法内的参数,就是传递给SQL的参数。

Mapper 接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为 key 值,可唯一定位一个 MapperStatement。在 Mybatis 中,每一个 、、、标签,都会被解析为一个 MapperStatement 对象。

Mapper接口里的方法,是不能重载的,因为是使用 全限名+方法名 的保存和寻找策略。Mapper 接口的工作原理是 JDK 动态代理,Mybatis 运行时会使用 JDK 动态代理为 Mapper 接口生成代理对象 proxy,代理对象会拦截接口方法,转而执行 MapperStatement 所代表的 sql,然后将 sql 执行结果返回。

八、模糊查询like语句

Mapper接口中定义方法:

List<Item> findItemsByName(@Param("name") String name);

Mapper XML文件中编写SQL查询:

<select id="findItemsByName" resultType="Item">
  SELECT * FROM item WHERE name LIKE CONCAT('%', #{name}, '%')
</select>

CONCAT(‘%’, #{name}, ‘%’)用于拼接SQL语句中的模糊查询字符串。
而SQL语句中拼接通配符,会有SQL注入风险:

<select id="findItemsByName" resultType="Item">
  SELECT * FROM item WHERE name LIKE"%"#{name}"%"
</select>
九、#{}和${}的区别

#{} 是预编译处理,${}是字符串替换。
Mybatis 在处理#{}时,会将 sql 中的#{}替换为’?'号,调用PreparedStatement的set方法来赋值;
Mybatis 在处理${}时,就是把${}替换成变量的值。
使用#{}可以有效的防止 SQL 注入,提高系统安全性。

十、二级缓存

二级缓存的和一级缓存功能一样,第一次查询,会将数据放入缓存中,然后第二次查询则会直接去缓存中取。
一级缓存是基于sqlSession的,二级缓存是基于mapper文件的namespace的,也就是说多个sqlSession可以共享一个mapper中的二级缓存区域,并且如果两个mapper的namespace 相同,即使是两个mapper执行sql查询到的数据,也存储在相同的二级缓存区域中。
在这里插入图片描述
开启二级缓存
一级缓存默认开启,但二级缓存需要手动开启。
首先在全局配置文件sqlMapConfig.xml文件中加入配置:

<!--开启二级缓存-->
<settings>
   <setting name="cacheEnabled" value="true"/>
</settings>

然后在UserMapper.xml文件中开启缓存(若是基于注解形式进行查询,可以在mapper查询接口上添加@CacheNamespace注解开启二级缓存)。
要进行二级缓存的Pojo类必须实现Serializable接口
方式一:在xml文件中配置:

<mapper namespace="com.demo.mapper.UserMapper">
   <!-- 定义二级缓存 -->
    <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
    <select>select * from table</select>
    <delete>delete from table where id = #{id}</delete>
    ...........
</mapper>

清除策略有:

LRU – 最近最少使用:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
默认的清除策略是 LRU。

方式二:@CacheNamespace

@Mapper
@CacheNamespace(eviction = FifoCache.class, flushInterval = 10000, size = 500, readWrite = true)
public interface UserMapper {
    // 定义 SQL 映射语句
}
案例实战

MybatisPlus整合Redis实现分布式二级缓存
MyBatis 内置的实现 PerpetualCache在分布式环境下存在问题,无法使用,因此使用implementation属性用于指定自定义缓存实现类,接下来整合Redis来实现分布式的二级缓存。
1.pom文件:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.4.1</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.24.3</version>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.22</version>
</dependency>

配置文件:

mybatis-plus:
    mapper-locations: classpath:mybatis/mapper/*.xml
    configuration:
      cache-enabled: true

Mapper接口上开启二级缓存:

@CacheNamespace(
   implementation = MybatisRedisCache.class,
   eviction = MybatisRedisCache.class)
@Mapper
public interface UserMapper extends BaseMapper<User> {

}

或者使用xml映射,UserMapper.xml加入:

<?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.lagou.mapper.IUserMapper">
    <!--二级缓存类地址-->
	<cache type="org.mybatis.caches.redis.RedisCache" />
	<select id="findAll" resultType="com.lagou.pojo.User" useCache="true">
	    select * from user
	</select> 
</mapper>

配置RedisTemplate:

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@Configuration
@EnableCaching
public class RedisConfiguration {
    private static final StringRedisSerializer STRING_SERIALIZER = new StringRedisSerializer();
    private static final GenericJackson2JsonRedisSerializer JACKSON__SERIALIZER = new GenericJackson2JsonRedisSerializer();
    @Bean
    @Primary
    public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
        //设置缓存过期时间
        RedisCacheConfiguration redisCacheCfg = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(STRING_SERIALIZER))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(JACKSON__SERIALIZER));
        return RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
                .cacheDefaults(redisCacheCfg)
                .build();
    }
    @Bean
    @Primary
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        // 配置redisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        // key序列化
        redisTemplate.setKeySerializer(STRING_SERIALIZER);
        // value序列化
        redisTemplate.setValueSerializer(JACKSON__SERIALIZER);
        // Hash key序列化
        redisTemplate.setHashKeySerializer(STRING_SERIALIZER);
        // Hash value序列化
        redisTemplate.setHashValueSerializer(JACKSON__SERIALIZER);
        // 设置支持事务
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
    @Bean
    public RedisSerializer<Object> redisSerializer() {
        //创建JSON序列化器
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //必须设置,否则无法将JSON转化为对象,会转化成Map类型
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        return new GenericJackson2JsonRedisSerializer(objectMapper);
    }
}

自定义缓存类:

import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.Cache;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.connection.RedisServerCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
@Slf4j
public class MybatisRedisCache implements Cache {
    // redisson 读写锁
    private final RReadWriteLock redissonReadWriteLock;
    // redisTemplate
    private final RedisTemplate redisTemplate;
    // 缓存Id
    private final String id;
    //过期时间 10分钟
    private final long expirationTime = 1000*60*10;
    public MybatisRedisCache(String id) {
        this.id = id;
        //获取redisTemplate
        this.redisTemplate = SpringUtil.getBean(RedisTemplate.class);
        //创建读写锁
        this.redissonReadWriteLock = SpringUtil.getBean(RedissonClient.class).getReadWriteLock("mybatis-cache-lock:"+this.id);
    }
    @Override
    public void putObject(Object key, Object value) {
        //使用redis的Hash类型进行存储
        redisTemplate.opsForValue().set(getCacheKey(key),value,expirationTime, TimeUnit.MILLISECONDS);
    }
    @Override
    public Object getObject(Object key) {
        try {
            //根据key从redis中获取数据
            Object cacheData = redisTemplate.opsForValue().get(getCacheKey(key));
            log.debug("[Mybatis 二级缓存]查询缓存,cacheKey={},data={}",getCacheKey(key), JSONUtil.toJsonStr(cacheData));
            return cacheData;
        } catch (Exception e) {
            log.error("缓存出错",e);
        }
        return null;
    }
    @Override
    public Object removeObject(Object key) {
        if (key != null) {
            log.debug("[Mybatis 二级缓存]删除缓存,cacheKey={}",getCacheKey(key));
            redisTemplate.delete(key.toString());
        }
        return null;
    }
    @Override
    public void clear() {
        log.debug("[Mybatis 二级缓存]清空缓存,id={}",getCachePrefix());
        Set keys = redisTemplate.keys(getCachePrefix()+":*");
        redisTemplate.delete(keys);
    }
    @Override
    public int getSize() {
        Long size = (Long) redisTemplate.execute((RedisCallback<Long>) RedisServerCommands::dbSize);
        return size.intValue();
    }
    @Override
    public ReadWriteLock getReadWriteLock() {
        return this.redissonReadWriteLock;
    }
    @Override
    public String getId() {
        return this.id;
    }
    public String getCachePrefix(){
        return "mybatis-cache:%s".formatted(this.id);
    }
    private String getCacheKey(Object key){
        return getCachePrefix()+":"+key;
    }
}

最后调用接口执行SQL即可测试缓存效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1906754.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

虽然我不会,但不能没有

大家好&#xff0c;才是真的好。 众所周知&#xff0c;从Notes/Domino R9版本到12版本&#xff0c;增加了不少的新功能和新任务&#xff0c;例如备份还原、自动更新、AD目录同步、自动证书管理、Nomad等等……我这里都要快写不下了。 不过&#xff0c;这些新功能、新任务&…

《SoC设计方法与实现》:全面掌握系统芯片设计精髓(可下载)

SoC&#xff08;System on Chip&#xff0c;系统级芯片&#xff09;设计是一项复杂而精细的工程活动&#xff0c;它涉及到将一个完整的电子系统的所有组件集成到一个单一的芯片上&#xff0c;包括处理器核心、内存、输入/输出端口以及可能的其他功能模块。这种集成不仅要求设计…

uniapp/Android App上架三星市场需要下载所需要的SDK

只需添加以下一个权限在AndroidManifest.xml <uses-permission android:name"com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY"/>uniapp开发的&#xff0c;需要在App权限配置中加入以上的额外权限&#xff1a;

基于大数据技术Hadoop的气象分析可视化大屏设计和实现

博主介绍&#xff1a;硕士研究生&#xff0c;专注于信息化技术领域开发与管理&#xff0c;会使用java、标准c/c等开发语言&#xff0c;以及毕业项目实战✌ 从事基于java BS架构、CS架构、c/c 编程工作近16年&#xff0c;拥有近12年的管理工作经验&#xff0c;拥有较丰富的技术架…

Django QuerySet对象,exclude()方法

模型参考上一章内容&#xff1a; Django QuerySet对象&#xff0c;filter()方法-CSDN博客 exclude()方法&#xff0c;用于排除符合条件的数据。 1&#xff0c;添加视图函数 Test/app11/views.py from django.shortcuts import render from .models import Postdef index(re…

智能遥测终端机RTU-精确监控 智能运维

智能遥测终端机RTU是物联网领域中一种重要的设备&#xff0c;它的出现无疑为远程监控和数据采集提供了强大的支持。计讯物联智能遥测终端机RTU具备数据采集、处理、通信和控制功能的设备&#xff0c;可以实现对远程设备的监控与控制。它在物联网系统中扮演着桥梁的角色&#xf…

单北斗定位智能终端提倡应用的重要性

单北斗定位智能终端的提倡应用具有重要意义&#xff0c;主要体现在以下几个方面&#xff1a; 一、国家安全与自主可控 自主可控&#xff1a;北斗卫星导航系统是我国自主研发的全球卫星导航系统&#xff0c;全面推广使用国产单北斗定位智能终端&#xff0c;意味着我们将更加依…

希亦、小吉、觉飞内衣洗衣机值得买吗?王牌对决测评还不来看看!

内衣洗衣机是近几年新兴的家电产品&#xff0c;以清洁效果好、除菌能力强&#xff0c;被很多人种草入手了&#xff01;但网上有不少人虽感兴趣&#xff0c;但不清楚如何选。担心买到质量差&#xff0c;清洗不干净的产品。所以为了帮助大家可以更好的了解哪个品牌的内衣洗衣机比…

bash: redi-cli: 未找到命令...

问题描述 在执行命令&#xff1a;redi-cli --bigkeys 提示&#xff1a;bash: redi-cli: 未找到命令... 确定服务器是否有Redis进程 ps -ef | grep redis查找Redis 文件信息 find / -name "redis-*"进入到当前目录 cd /usr/bin/再次执行命令 涉及redis-cli 连…

(三)前端javascript中的数据结构之链表上

在js中&#xff0c;没有为我们提供原生的数据结构支持的&#xff0c;但是在java中是有提供的。所以需要我们去模拟这种结构实现。 链表中最关键的一个元素&#xff0c;就是头节点&#xff0c;头节点不存储数据&#xff0c;指向第一个节点链表中几乎所有的操作都要从头结点开始。…

提高交易决策质量,Anzo Capital昂首资本只需两个交易策略

要想提高交易决策质量&#xff0c;其实很简单&#xff0c;Anzo Capital昂首资本只需两个交易策略&#xff0c;结合价格行为和VSA(成交量与价格分析)就可以达成我们的目的。首先&#xff0c;理解这两个概念&#xff1a; 1. 价格行为&#xff1a;价格行为是市场价格变动的方式&a…

js逆向研究【响应结果解密思路与案例实战】

什么是响应结果加密 我们在爬虫过程中&#xff0c;抓包之后&#xff0c;针对内容关键词搜索无法定位到数据接口&#xff0c;并在响应的接口内发现有编码/不可读的长字符串等&#xff0c;我们可以判定其为响应结果加密。 如何针对将响应结果还原为可读的数据 如果响应结果有特…

高中毕业生,学历低可以考PMP吗?

PMP认证与学历无关&#xff0c;但需要注意报考条件&#xff1a; 学历&#xff1a;满23周岁/高中毕业5年以上/大专毕业3年以上&#xff0c;满足其中一个即可&#xff1b; 证明&#xff1a;必须有35学时&#xff08;PDU&#xff09;证明&#xff0c;即项目管理培训或学习证明&a…

Java | Leetcode Java题解之第220题存在重复元素III

题目&#xff1a; 题解&#xff1a; class Solution {public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {int n nums.length;Map<Long, Long> map new HashMap<Long, Long>();long w (long) t 1;for (int i 0; i < n; i) {long i…

6、Redis系统-数据结构-04-Hash

四、哈希表&#xff08;Hashtable&#xff09; 哈希表是一种高效的键值对数据结构&#xff0c;通过散列函数将键映射到表中的位置&#xff0c;实现快速的插入、删除和查找操作。Redis 广泛使用哈希表来实现 Hash 对象和数据库的键值存储。以下将从结构设计、哈希冲突与链式哈希…

【学术会议征稿】第五届计算机工程与智能控制学术会议(ICCEIC 2024)

第五届计算机工程与智能控制学术会议&#xff08;ICCEIC 2024) 2024 5th International Conference on Computer Engineering and Intelligent Control 第五届计算机工程与智能控制学术会议&#xff08;ICCEIC 2024&#xff09;将于2024年10月18日至22日在广州举办&#xff0…

04-Haproxy搭建Web群集

理论讲解 Haproxy 是目前比较流行的一种群集调度工具&#xff0c;同类群集调度工具有很多&#xff0c;如LVS 和Nginx。相比较而言&#xff0c;LVS 性能最好&#xff0c;但是搭建相对复杂:Nginx的upstream模块支持群集功能&#xff0c;但是对群集节点健康检查功能不强&#xff…

tiff图片怎么转jpg?值得介绍的四种tiff转jpg方法

tiff图片怎么转jpg&#xff1f;jpg使用有损压缩技术&#xff0c;可以显著减小文件大小。这使得jpg文件更适合在网络上传输和在设备上存储。而且&#xff0c;PG格式被各种浏览器、图像查看器、编辑软件以及社交媒体平台广泛支持&#xff0c;使得分享和查看图片更加方便。如果需要…

tableau条形图绘制 - 2

tableau条形图绘制 1. 条形图绘制-11.1 创建工作表1.2 修改工作表名称1.3 条形图绘制1.4 显示标签1.5 行列转换 2. 条形图绘制-22.1 新建工作表2.2 修改工作表名称2.3 条形图绘制2.4 价格度量选平均值2.5 标签度量选平均值2.6 升序&#xff0c;整个视图显示2.7 行列转换 3. 堆积…

叹为观止|四款让人赞不绝口的优质软件,越用越上瘾

不说闲话直接上狠货&#xff0c;下面神仙软件&#xff0c;都值得使用。 Smart Defrag 说起电脑运行慢或者抽风&#xff0c;磁盘碎片就是让电脑变得又卡又不稳定的元凶之一。 不过Smart Defrag就算是新手小白也能操作&#xff0c;它里面藏着一个超强的碎片整理引擎&#xff0…