如何用Word做网站单页,广州网站建设系统,乡村振兴网站建设,网站基本流程MyBatis的知识收获 一、概述二、获取自动生成的(主)键值三、将sql执行结果封装为目标返回对象的方式和原理四、延迟加载实现原理五、批量插入六、自带分页与分页插件原理七、Mapper(Dao)接口与XML映射文件关系八、模糊查询like语句九、#{}和${}的区别十、二级缓存案例实战 一、… MyBatis的知识收获 一、概述二、获取自动生成的(主)键值三、将sql执行结果封装为目标返回对象的方式和原理四、延迟加载实现原理五、批量插入六、自带分页与分页插件原理七、Mapper(Dao)接口与XML映射文件关系八、模糊查询like语句九、#{}和${}的区别十、二级缓存案例实战 一、概述
最近几天公司项目开发上线完成做个收获总结吧~ 今天记录MyBatis的收获和提升。
二、获取自动生成的(主)键值
insert 方法总是返回一个 int 值 这个值代表的是插入的行数。若表的主键id采用自增长策略自动生成的键值在 insert 方法执行完后可以被设置到传入的参数对象中。 insert id”insertUser” usegeneratedkeys”true” keyproperty” id”insert into table_name (name, age) values (#{name}, #{age})
/insertUser 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 配置文件中可以配置是否启用延迟加载 lazyLoadingEnabledtrue|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 idinsertBatchINSERT INTO userTable (column1, column2, ...)VALUESforeach collectionlist itemitem indexindex 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 中每一个 select、insert、update、delete标签都会被解析为一个 MapperStatement 对象。
Mapper接口里的方法是不能重载的因为是使用 全限名方法名 的保存和寻找策略。Mapper 接口的工作原理是 JDK 动态代理Mybatis 运行时会使用 JDK 动态代理为 Mapper 接口生成代理对象 proxy代理对象会拦截接口方法转而执行 MapperStatement 所代表的 sql然后将 sql 执行结果返回。
八、模糊查询like语句
Mapper接口中定义方法
ListItem findItemsByName(Param(name) String name);Mapper XML文件中编写SQL查询
select idfindItemsByName resultTypeItemSELECT * FROM item WHERE name LIKE CONCAT(%, #{name}, %)
/selectCONCAT(‘%’, #{name}, ‘%’)用于拼接SQL语句中的模糊查询字符串。 而SQL语句中拼接通配符会有SQL注入风险
select idfindItemsByName resultTypeItemSELECT * FROM item WHERE name LIKE%#{name}%
/select九、#{}和${}的区别
#{} 是预编译处理${}是字符串替换。 Mybatis 在处理#{}时会将 sql 中的#{}替换为’?号调用PreparedStatement的set方法来赋值 Mybatis 在处理${}时就是把${}替换成变量的值。 使用#{}可以有效的防止 SQL 注入提高系统安全性。
十、二级缓存
二级缓存的和一级缓存功能一样第一次查询会将数据放入缓存中然后第二次查询则会直接去缓存中取。 一级缓存是基于sqlSession的二级缓存是基于mapper文件的namespace的也就是说多个sqlSession可以共享一个mapper中的二级缓存区域并且如果两个mapper的namespace 相同即使是两个mapper执行sql查询到的数据也存储在相同的二级缓存区域中。 开启二级缓存 一级缓存默认开启但二级缓存需要手动开启。 首先在全局配置文件sqlMapConfig.xml文件中加入配置
!--开启二级缓存--
settingssetting namecacheEnabled valuetrue/
/settings然后在UserMapper.xml文件中开启缓存若是基于注解形式进行查询可以在mapper查询接口上添加CacheNamespace注解开启二级缓存。 要进行二级缓存的Pojo类必须实现Serializable接口 方式一在xml文件中配置
mapper namespacecom.demo.mapper.UserMapper!-- 定义二级缓存 --cache evictionLRU flushInterval60000 size512 readOnlytrue/selectselect * from table/selectdeletedelete 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文件
dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.5.4.1/version
/dependency
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId
/dependency
dependencygroupIdorg.redisson/groupIdartifactIdredisson-spring-boot-starter/artifactIdversion3.24.3/version
/dependency
dependencygroupIdcn.hutool/groupIdartifactIdhutool-all/artifactIdversion5.8.22/version
/dependency配置文件
mybatis-plus:mapper-locations: classpath:mybatis/mapper/*.xmlconfiguration:cache-enabled: trueMapper接口上开启二级缓存
CacheNamespace(implementation MybatisRedisCache.class,eviction MybatisRedisCache.class)
Mapper
public interface UserMapper extends BaseMapperUser {}或者使用xml映射UserMapper.xml加入
?xml version1.0 encodingUTF-8?
!DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN
http://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.lagou.mapper.IUserMapper!--二级缓存类地址--cache typeorg.mybatis.caches.redis.RedisCache /select idfindAll resultTypecom.lagou.pojo.User useCachetrueselect * 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();BeanPrimarypublic 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();}BeanPrimaryConditionalOnMissingBean(name redisTemplate)public RedisTemplateString, Object redisTemplate(RedisConnectionFactory factory) {// 配置redisTemplateRedisTemplateString, 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;}Beanpublic RedisSerializerObject 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;// redisTemplateprivate final RedisTemplate redisTemplate;// 缓存Idprivate final String id;//过期时间 10分钟private final long expirationTime 1000*60*10;public MybatisRedisCache(String id) {this.id id;//获取redisTemplatethis.redisTemplate SpringUtil.getBean(RedisTemplate.class);//创建读写锁this.redissonReadWriteLock SpringUtil.getBean(RedissonClient.class).getReadWriteLock(mybatis-cache-lock:this.id);}Overridepublic void putObject(Object key, Object value) {//使用redis的Hash类型进行存储redisTemplate.opsForValue().set(getCacheKey(key),value,expirationTime, TimeUnit.MILLISECONDS);}Overridepublic 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;}Overridepublic Object removeObject(Object key) {if (key ! null) {log.debug([Mybatis 二级缓存]删除缓存,cacheKey{},getCacheKey(key));redisTemplate.delete(key.toString());}return null;}Overridepublic void clear() {log.debug([Mybatis 二级缓存]清空缓存,id{},getCachePrefix());Set keys redisTemplate.keys(getCachePrefix():*);redisTemplate.delete(keys);}Overridepublic int getSize() {Long size (Long) redisTemplate.execute((RedisCallbackLong) RedisServerCommands::dbSize);return size.intValue();}Overridepublic ReadWriteLock getReadWriteLock() {return this.redissonReadWriteLock;}Overridepublic String getId() {return this.id;}public String getCachePrefix(){return mybatis-cache:%s.formatted(this.id);}private String getCacheKey(Object key){return getCachePrefix():key;}
}最后调用接口执行SQL即可测试缓存效果。