SpringBoot下mybatis-一级、二级缓存测试及总结
- 格式:docx
- 大小:9.46 KB
- 文档页数:3
mybatis⼀级缓存和⼆级缓存相关源码⽬录mybatis简介MyBatis is a first class persistence framework with support for custom SQL, stored procedures and advanced mappings.MyBatis eliminates almost all of the JDBC code and manual setting of parameters and retrieval of results.MyBatis can use simple XML or Annotations for configuration and map primitives, Map interfaces and Java POJOs (Plain Old Java Objects) to database records. debug版本:mybatis:3.4.5⾃我理解mybatis代理Dao类,当运⾏Dao中⽅法时,根据⽅法查找相应的sql,然后去执⾏sql,来提供返回值。
初始化⼀级缓存和⼆级缓存程序主函数因为mybatis采⽤的JDK反向代理,来代理Dao类。
如果你想看代理Dao类具体实现,可以⽤⼀下⽅式# idea打断点,查找运⾏java程序的pid# 管理员⾝份运⾏jdk中sa-jdi.jar 主函数sun.jvm.hotspot.HSDB,来attach 那个pid# 就可以查看Java程序运⾏到断点处的状态了,查找代理类# JDK代理⽅法,命名为 $Proxy + 数字,不好查找,可以在断点前,输出代理类的名字sudo java -classpath './jdk1.8.0_172/lib/sa-jdi.jar' sun.jvm.hotspot.HSDB代理Dao类,主要运⾏了MapperProxy.invoke⽅法Object.class.equals(method.getDeclaringClass()) ⽤来判断是否调⽤的是Object.class中⽅法,isDefaultMethod(method) ⽤来判断是否是Java8中Interface 中新特性default修饰的⽅法,MapperMethod.execute判断执⾏的是insert,update,delete,select,flush中的哪⼀个,insert,update,delete返回的都是影响的⾏数,select才需要特殊处理返回数据,⾄于flush是执⾏有@Flush注解的⽅法是才去执⾏。
缓存(⼀级缓存和⼆级缓存)缓存可以将数据保存在内存中,是互联⽹系统常常⽤到的。
⽬前流⾏的缓存服务器有 MongoDB、Redis、Ehcache 等。
缓存是在计算机内存上保存的数据,读取时⽆需再从磁盘读⼊,因此具备快速读取和使⽤的特点。
和⼤多数持久化框架⼀样,MyBatis 提供了⼀级缓存和⼆级缓存的⽀持。
默认情况下,MyBatis 只开启⼀级缓存。
⼀级缓存⼀级缓存是基于 PerpetualCache(MyBatis⾃带)的 HashMap 本地缓存,作⽤范围为 session 域内。
当 session flush(刷新)或者close(关闭)之后,该 session 中所有的 cache(缓存)就会被清空。
在参数和 SQL 完全⼀样的情况下,我们使⽤同⼀个 SqlSession 对象调⽤同⼀个 mapper 的⽅法,往往只执⾏⼀次 SQL。
因为使⽤SqlSession 第⼀次查询后,MyBatis 会将其放在缓存中,再次查询时,如果没有刷新,并且缓存没有超时的情况下,SqlSession 会取出当前缓存的数据,⽽不会再次发送 SQL 到数据库。
由于 SqlSession 是相互隔离的,所以如果你使⽤不同的 SqlSession 对象,即使调⽤相同的 Mapper、参数和⽅法,MyBatis 还是会再次发送 SQL 到数据库执⾏,返回结果。
⽰例:WebsiteMapperpublic Website selectWebsiteById(int id);WebsiteMapper.xml<select id="selectWebsiteById"resultType="net.biancheng.po.Website">SELECT * FROM websiteWHERE id=#{id}</select>测试代码public class Test {public static Logger logger = Logger.getLogger(Test.class);public static void main(String[] args) throws IOException {InputStream config = Resources.getResourceAsStream("mybatis-config.xml"); // 根据配置⽂件构建SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config);SqlSession ss = ssf.openSession();Website site = ss.selectOne("net.biancheng.mapper.WebsiteMapper.selectWebsiteById", 1);logger.debug("使⽤同⼀个sqlsession再执⾏⼀次");Website site2 = ss.selectOne("net.biancheng.mapper.WebsiteMapper.selectWebsiteById", 1);// 请注意,当我们使⽤⼆级缓存的时候,sqlSession调⽤了 commit⽅法后才会⽣效mit();logger.debug("现在创建⼀个新的SqlSeesion对象在执⾏⼀次");SqlSession ss2 = ssf.openSession();Website site3 = ss2.selectOne("net.biancheng.mapper.WebsiteMapper.selectWebsiteById", 1);// 请注意,当我们使⽤⼆级缓存的时候,sqlSession调⽤了 commit⽅法后才会⽣效mit();}}运⾏结果DEBUG [main] - ==> Preparing: SELECT * FROM website WHERE id=?DEBUG [main] - ==> Parameters: 1(Integer)DEBUG [main] - <== Total: 1DEBUG [main] - 使⽤同⼀个sqlsession再执⾏⼀次DEBUG [main] - 现在创建⼀个新的SqlSeesion对象在执⾏⼀次DEBUG [main] - ==> Preparing: SELECT * FROM website WHERE id=?DEBUG [main] - ==> Parameters: 1(Integer)DEBUG [main] - <== Total: 1从运⾏结果可以看出,第⼀个 SqlSession 实际只发⽣过⼀次查询,⽽第⼆次查询就从缓存中取出了,也就是 SqlSession 层⾯的⼀级缓存。
【Mybatis】Mybatis实战2(⼀对⼀、⼀对多、多对多的设计及实现,⾼级特性及⼆级缓存)6).多表查询-“⼀对多”(表设计、实体设计、DAO(mapper)设计)(1)关联关系操作(⼀对多)①表设计:以员⼯和部门表为例思想: 1个员⼯对应1个部门,1个部门对应多个员⼯添加数据原则:先添加没有外键的数据(部门信息),再添加存在外键的数据(员⼯信息)注意:将外键添加在n的⼀⽅部门表:create table t_dept(id varchar2(36) primary key,name varchar2(50));员⼯表:create table t_emp(id varchar2(36) primary key,name varchar2(50),age number(3),salary number(10,2),dept_id references t_dept(id));②实体设计a. 在实体中添加关系属性,来表⽰实体之间的关系(对应表数据的关系)b. 在N的⼀⽅添加1的⼀个关系属性。
c. 在1的⼀⽅添加N的⼀个List的关系属性DAO:(MyBatis如何查询两张表信息)需求1:查询员⼯信息(⼯号,名字,年龄,薪资,所属部门的编号和名称)根据员⼯⼯号?DAO接⼝⽅法:public Emp selectById(String id);Mapper⽂件:①SQL:select e.id,,e.age,e.salary,d.id, from t_emp e left join t_dept d on e.dept_id = d.id where e.id = '5';②参数③将查询结果映射成⼀个实体对象特点: 如果关系属性是”1” ,使⽤ <association></association>需求2:根据id查询部门信息,及其内部的所有员⼯信息?DAO接⼝⽅法:public Dept selectById(String id);Mapper⽂件中①SQL:select d.id,,e.id as eid, as ename,e.age as eage,e.salary as salary from t_dept d left join t_emp e on d.id = e.dept_idwhere d.id = ?;②参数绑定③结果映射:ReusultMap映射集合关系属性特点: 关系属性是”n”个的集合 ,使⽤ <collection></ collection >7).多表查询-“⼀对⼀”(表设计、实体设计、DAO(mapper)设计)关联关系操作(⼀对⼀)例如:需求: 学⽣电脑管理系统①库表设计表⽰1对1的关系a. 添加外键(那张表添加都可以)①从业务的⾓度分析,后添加的数据对应的表。
mybatis一级缓存原理MyBatis一级缓存原理解析一级缓存介绍•一级缓存是MyBatis默认开启的缓存机制,通过在内存中缓存查询结果,提高查询性能。
•一级缓存在同一个Session的范围内生效,不同Session之间的缓存不共享。
缓存的工作原理1.首先,当执行一个查询语句时,MyBatis会检查是否开启了一级缓存。
2.如果开启了一级缓存,MyBatis会先从缓存中查找是否存在相同的查询语句及其参数的缓存结果。
3.如果缓存中存在,则直接返回缓存结果,不再访问数据库。
4.如果缓存中不存在,则执行查询操作,将查询结果缓存起来。
5.缓存结果是一个对象,包含查询的结果集及其所对应的映射关系。
缓存的生命周期•缓存存在于SqlSession中,当SqlSession关闭时,缓存中的数据也被清空。
•当执行了插入、更新或删除操作时,会清空对应的缓存,保证缓存的数据与数据库数据一致。
缓存的失效机制•MyBatis通过以下方式实现缓存的失效:–当执行了插入、更新或删除操作时,对应的缓存会被清空。
–当手动调用clearCache()方法时,会清空全部缓存。
–当开启了flushCache属性时,在执行查询操作前,会先清空全部缓存。
缓存的注意事项•一级缓存只在同一Session中生效,不同Session之间的缓存不共享。
•更新操作会清空对应的缓存,所以在更新之后的查询中,会重新执行查询操作。
•如果需要在同一Session中多次查询相同的数据,可以考虑使用缓存提高性能。
•当查询的数据频繁变化时,缓存可能会导致数据不一致的问题,需要慎重使用。
结语通过本文的介绍,我们对MyBatis一级缓存的原理有了更深入的了解。
一级缓存在同一个Session中起到了提高查询性能的作用,但也需要注意缓存的生命周期与失效机制,以及潜在的数据一致性问题。
一级缓存的使用场景•查询频率高:如果多次查询的数据在同一个Session中不会发生变化,那么将查询结果缓存起来可以避免多次访问数据库,提升性能。
mybatis中的一二级缓存的实现原理Mybatis是一个优秀的ORM(ObjectRelationalMapping)框架,它提供了一种简单的方式来访问数据库。
在Mybatis中,缓存是一个非常重要的概念,它可以提高应用程序的性能。
Mybatis中的缓存分为一级缓存和二级缓存,下面将详细介绍它们的实现原理。
一级缓存Mybatis中的一级缓存是指在同一个SqlSession中执行相同的SQL语句时,返回的结果会被缓存起来,下一次执行相同的SQL语句时,可以直接从缓存中获取结果,而不需要再次查询数据库。
一级缓存的实现是基于内存的,缓存的生命周期与SqlSession的生命周期一致。
一级缓存是Mybatis默认开启的,可以通过SqlSession的clearCache()方法来清空一级缓存。
二级缓存Mybatis中的二级缓存是指在同一个应用程序中多个SqlSession之间共享缓存数据,它的作用是提高应用程序的性能。
二级缓存的实现是基于缓存机制的,缓存的生命周期与应用程序的生命周期一致。
二级缓存是通过使用单独的缓存空间来实现的,可以使用第三方缓存框架如Ehcache、Redis等来实现。
Mybatis中的二级缓存的使用需要注意以下几点:1. Mapper文件中需要配置开启二级缓存<cacheeviction='LRU'flushInterval='100000'size='1024'readOnly='true'/>2. 对于需要缓存的对象,需要实现Serializable接口3. 在不同的SqlSession中使用同一个Mapper,才能共享缓存数据4. 对于更新、插入、删除操作,会清空相关的缓存总结:Mybatis中的缓存是提高应用程序性能的重要手段,一级缓存和二级缓存的实现原理不同,使用时需要注意它们的区别和限制条件。
Mybatis⼀级缓存与⼆级缓存的实现mybatis缓存mybatis作为⼀个流⾏的持久化⼯具,缓存必然是缺少不了的组件。
通过这篇⽂章,就让我们来了解⼀下mybatis的缓存。
mybatis缓存类型说起mybatis的缓存,了解过的同学都知道,mybatis中可以有两种缓存类型:第⼀种,我们通常称为以及缓存,或者sqlSession级别的缓存,这种缓存是mybatis⾃带的,如果mapper中的配置都是默认的话,那么⼀级缓存也是默认开启的。
第⼆种,就是⾮sqlSession级别的缓存了,我们通常称为⼆级缓存,mybatis中的⼆级缓存需要实现Cache接⼝,并且配置在mapper中,要先开启的话,需要⼀些配置,下⾯我们会详细说到。
⼀级缓存作为mybatis⾃带的缓存,我们通过代码来分析⼀下其原理。
⾸先,我们来看下⼀级缓存的效果。
测试代码:@Testpublic void test_Cache() throws Exception {InputStream input = Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(input);SqlSession sqlSession = factory.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);System.out.println("the first query : ");mapper.queryAllUsers();System.out.println("====================================");System.out.println("the second query : ");mapper.queryAllUsers();mit();}mapper配置如下,我们采⽤默认配置:<select id="queryAllUsers" resultType="User">select * from hwc_users</select>运⾏结果如下:Created connection 1191654595.Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@470734c3]==> Preparing: select * from hwc_users==> Parameters:<== Columns: id, name, age, email<== Row: 1, 胡⽂超, 35, huwenchao@<== Row: 2, 胡⽂超, 35, huwenchao@<== Row: 3, 胡⽂超, 35, huwenchao@<== Row: 4, 胡⽂超, 35, huwenchao@<== Row: 5, 胡⽂超, 35, huwenchao@<== Row: 6, 胡⽂超, 35, huwenchao@<== Row: 7, 胡⽂超, 35, huwenchao@<== Row: 8, 胡⽂超, 35, huwenchao@<== Row: 9, 胡⽂超, 35, huwenchao@<== Total: 9====================================the second query :Cache Hit Ratio [erMapper]: 0.0Process finished with exit code 0从上述结果可以看到,第⼆次查询并没有从数据库获取,并且没有从⼆级缓存中获取,由此可见,默认配置情况下,同⼀个sqlSession中会默认使⽤mybatis的⼀级缓存。
mybatis的缓存机制及⽤例介绍在实际的项⽬开发中,通常对数据库的查询性能要求很⾼,⽽mybatis提供了查询缓存来缓存数据,从⽽达到提⾼查询性能的要求。
mybatis的查询缓存分为⼀级缓存和⼆级缓存,⼀级缓存是SqlSession级别的缓存,⼆级缓存时mapper级别的缓存,⼆级缓存是多个SqlSession共享的。
mybatis通过缓存机制减轻数据压⼒,提⾼数据库性能。
⼀级缓存:mybatis的⼀级缓存是SQLSession级别的缓存,在操作数据库时需要构造SqlSession对象,在对象中有⼀个HashMap⽤于存储缓存数据,不同的SqlSession之间缓存数据区域(HashMap)是互相不影响的。
⼀级缓存的作⽤域是SqlSession范围的,当在同⼀个SqlSession中执⾏两次相同的sql语句时,第⼀次执⾏完毕会将数据库中查询的数据写到缓存(内存)中,第⼆次查询时会从缓存中获取数据,不再去底层进⾏数据库查询,从⽽提⾼了查询效率。
需要注意的是:如果SqlSession执⾏了DML操作(insert、update、delete),并执⾏commit()操作,mybatis则会清空SqlSession中的⼀级缓存,这样做的⽬的是为了保证缓存数据中存储的是最新的信息,避免出现脏读现象。
当⼀个SqlSession结束后该SqlSession中的⼀级缓存也就不存在了,Mybatis默认开启⼀级缓存,不需要进⾏任何配置。
注意:Mybatis的缓存机制是基于id进⾏缓存,也就是说Mybatis在使⽤HashMap缓存数据时,是使⽤对象的id作为key,⽽对象作为value保存例⼦说明:⼯程架构图:pom.xml<project xmlns="/POM/4.0.0" xmlns:xsi="/2001/XMLSchema-instance" xsi:schemaLocation="/POM/4.0.0 /xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion><groupId>com.gm.test</groupId><artifactId>MybatisCacheTest</artifactId><version>0.0.1-SNAPSHOT</version><packaging>war</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><!-- spring版本号 --><spring.version>4.3.10.RELEASE</spring.version><!-- mybatis版本号 --><mybatis.version>3.2.6</mybatis.version><!-- log4j⽇志⽂件管理包版本 --><slf4j.version>1.7.7</slf4j.version><log4j.version>1.2.17</log4j.version></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><!-- 表⽰开发的时候引⼊,发布的时候不会加载此包 --><scope>test</scope></dependency><!-- spring核⼼包 --><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-oxm</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>${spring.version}</version></dependency><!-- aop --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.4</version></dependency><!-- mybatis核⼼包 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>${mybatis.version}</version></dependency><!-- mybatis/spring包 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.2.2</version></dependency><!-- 导⼊java ee jar 包 --><dependency><groupId>javax</groupId><artifactId>javaee-api</artifactId><version>7.0</version></dependency><!-- 导⼊Mysql数据库链接jar包 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.30</version></dependency><!-- 导⼊dbcp的jar包,⽤来在applicationContext.xml中配置数据库 --> <dependency><groupId>commons-dbcp</groupId><artifactId>commons-dbcp</artifactId><version>1.2.2</version></dependency><!-- JSTL标签类 --><dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency><!-- ⽇志⽂件管理包 --><!-- log start --><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>${log4j.version}</version></dependency><!-- 格式化对象,⽅便输出⽇志 --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.1.41</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>${slf4j.version}</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>${slf4j.version}</version></dependency><!-- log end --></dependencies><build><plugins><plugin><artifactId>maven-war-plugin</artifactId><configuration><version>3.1</version></configuration></plugin></plugins></build></project>sql语句:CREATE TABLE employee(id INT(11) PRIMARY KEY AUTO_INCREMENT,loginname VARCHAR(18),PASSWORD VARCHAR(18),NAME VARCHAR(18) DEFAULT NULL,sex CHAR(2) DEFAULT NULL,age INT(11) DEFAULT NULL,phone VARCHAR(21),sal DOUBLE,state VARCHAR(18));INSERT INTO employee (loginname,PASSWORD,NAME,sex,age,phone,sal,state) VALUES('jack','123456','杰克','男',26,'12345678936',9800,'ACTIVE'); INSERT INTO employee (loginname,PASSWORD,NAME,sex,age,phone,sal,state) VALUES('rose','123456','露丝','⼥',21,'78965412395',6800,'ACTIVE'); INSERT INTO employee (loginname,PASSWORD,NAME,sex,age,phone,sal,state) VALUES('tom','123456','汤姆','男',25,'139********',8800,'ACTIVE'); INSERT INTO employee (loginname,PASSWORD,NAME,sex,age,phone,sal,state) VALUES('alice','123456','爱丽丝','⼥',20,'74185296375',5800,'ACTIVE'); log4j.properties:log4j.rootLogger=ERROR, stdout.gm.mapper.EmployeeMapper=DEBUGlog4j.appender.console=org.apache.log4j.ConsoleAppenderyout=org.apache.log4j.PatternLayoutyout.ConversionPattern=[%-12d{HH\:mm\:ss.SS}] [%p] %l %m%nlog4j.appender.stdout=org.apache.log4j.ConsoleAppenderyout=org.apache.log4j.PatternLayoutyout.ConversionPattern=%d %p [%c] - %m%nmybatis-config.xml:<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE configuration PUBLIC "-////DTD Config 3.0//EN" "/dtd/mybatis-3-config.dtd"><configuration><!-- 全局参数 --><settings><!-- 设置但JDBC类型为空时,某些驱动程序要指定值,default:OTHER,插⼊空值时不需要指定类型 --><setting name="jdbcTypeForNull" value="NULL" /><!-- 要使延迟加载⽣效必须配置下⾯两个属性 --><setting name="lazyLoadingEnabled" value="true" /><setting name="aggressiveLazyLoading" value="false" /><setting name="logImpl" value="LOG4J" /></settings><!-- <plugins> <plugin interceptor="com.manager.util.MybatisInterceptor"></plugin></plugins> --><environments default="mysql"><environment id="mysql"><!-- 指定事务管理类型,type="JDBC"指直接简单使⽤了JDBC的提交和回滚设置 --><transactionManager type="JDBC" /><!-- dataSource指数据源配置,POOLED是JDBC连接对象的数据源连接池的实现 --><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis" /><property name="username" value="root" /><property name="password" value="1qaz@wsx" /></dataSource></environment></environments><mappers><mapper resource="com/gm/mapper/EmployeeMapper.xml" /></mappers></configuration>Employee.java:package com.gm.domain;import java.io.Serializable;public class Employee implements Serializable {/****/private static final long serialVersionUID = 1L;private Integer id;private String loginname;private String password;private String name;private String sex;private String age;private String phone;private Double sal;private String state;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getLoginname() {return loginname;}public void setLoginname(String loginname) {this.loginname = loginname;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getName() {return name;}public void setName(String name) { = name;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public String getAge() {return age;}public void setAge(String age) {this.age = age;}public String getPhone() {return phone;}public void setPhone(String phone) {this.phone = phone;}public Double getSal() {return sal;}public void setSal(Double sal) {this.sal = sal;}public String getState() {return state;}public void setState(String state) {this.state = state;}}EmployeeMapper.xml:<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-////DTD Mapper 3.0//EN" "/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.gm.mapper.EmployeeMapper"><select id="selectEmployeeById" parameterType="int"resultType="com.gm.domain.Employee">SELECT * FROM employee WHERE id = #{id}</select><!-- 查询所有Employee --><select id="selectAllEmployee" parameterType="int"resultType="com.gm.domain.Employee">SELECT * FROM employee</select><!-- 根据id删除Employee --><delete id="deleteEmployeeById" parameterType="int">DELETE FROMemployee WHERE id = #{id}</delete></mapper>EmployeeMapper.java:package com.gm.mapper;import java.util.List;import com.gm.domain.Employee;public interface EmployeeMapper {// 根据id查询EmployeeEmployee selectEmployeeById(Integer id);// 查询所有EmployeeList<Employee> selectAllEmployee();// 根据id删除Employeevoid deleteEmployeeById(Integer id);}⼯⼚⼯具类:MySqlSessionFactory.java:package com.gm.factory;import java.io.IOException;import java.io.InputStream;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;public class MySqlSessionFactory {private static SqlSessionFactory sqlSessionFactory = null;static {try {InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);} catch (IOException e) {e.printStackTrace();}}// 获取SqlSession对象的静态⽅法public static SqlSession getSqlSession() {return sqlSessionFactory.openSession();}// 获取SqlSessionFactory的静态⽅法public static SqlSessionFactory getSessionFactory() {return sqlSessionFactory;}}执⾏⽅法类⼀:OneLevelCacheTest.javapackage com.gm.test;import org.apache.ibatis.session.SqlSession;import com.gm.domain.Employee;import com.gm.factory.MySqlSessionFactory;import com.gm.mapper.EmployeeMapper;public class OneLevelCacheTest {public void testCache1() {// 使⽤⼯⼚类获得SqlSession对象SqlSession sqlSession = MySqlSessionFactory.getSqlSession();// 获得EmployeeMapping对象EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);Employee employee1 = employeeMapper.selectEmployeeById(1);System.out.println(employee1);// 再次查询id为1的Employee对象,因为是同⼀个SqlSession,所以会从之前的⼀级缓存中查找数据Employee employee2 = employeeMapper.selectEmployeeById(1);System.out.println(employee2);sqlSession.close();}public static void main(String[] args) {OneLevelCacheTest t = new OneLevelCacheTest();t.testCache1();}public void testCache2() {// 使⽤⼯⼚类获得SqlSession对象SqlSession sqlSession = MySqlSessionFactory.getSqlSession();// 获得EmployeeMapping对象EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);Employee employee1 = employeeMapper.selectEmployeeById(1);System.out.println(employee1);// 执⾏delete操作employeeMapper.deleteEmployeeById(4);// commit提交mit();// 再次查询id为1的Employee对象,因为DML操作会清空sqlSession缓存,所以会再次执⾏select语句Employee employee2 = employeeMapper.selectEmployeeById(1);System.out.println(employee2);sqlSession.close();}public void testCache3() {// 使⽤⼯⼚类获得SqlSession对象SqlSession sqlSession = MySqlSessionFactory.getSqlSession();// 获得EmployeeMapping对象EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);Employee employee1 = employeeMapper.selectEmployeeById(1);System.out.println(employee1);// 关闭⼀级缓存sqlSession.close();// 再次访问,需要再次获取⼀级缓存,然后才能查找数据,否则会抛出异常sqlSession = MySqlSessionFactory.getSqlSession();// 再次获得EmployeeMapper对象employeeMapper = sqlSession.getMapper(EmployeeMapper.class);Employee employee2 = employeeMapper.selectEmployeeById(1);System.out.println(employee2);sqlSession.close();}public void testCache4() {// 使⽤⼯⼚类获得SqlSession对象SqlSession sqlSession = MySqlSessionFactory.getSqlSession();// 获得EmployeeMapping对象EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);Employee employee1 = employeeMapper.selectEmployeeById(1);System.out.println(employee1);// 关闭⼀级缓存sqlSession.close();// 再次访问,需要再次获取⼀级缓存,然后才能查找数据,否则会抛出异常sqlSession = MySqlSessionFactory.getSqlSession();// 再次获得EmployeeMapper对象employeeMapper = sqlSession.getMapper(EmployeeMapper.class);// 再次查询id为1的Employee对象,因为DML操作会清空sqlSession缓存,所以会再次执⾏select语句Employee employee2 = employeeMapper.selectEmployeeById(1);System.out.println(employee2);sqlSession.close();}}运⾏之后结果:2018-08-22 09:35:47,858 DEBUG [com.gm.mapper.EmployeeMapper.selectEmployeeById] - ==> Preparing: SELECT * FROM employee WHERE id = ? 2018-08-22 09:35:47,886 DEBUG [com.gm.mapper.EmployeeMapper.selectEmployeeById] - ==> Parameters: 1(Integer)2018-08-22 09:35:47,899 DEBUG [com.gm.mapper.EmployeeMapper.selectEmployeeById] - <== Total: 1com.gm.domain.Employee@56235b8ecom.gm.domain.Employee@56235b8e通过观察结果可以看出,在第⼀次查询id为1的Employee对象时执⾏了⼀条select语句,但是第⼆次获取id为1的Employee对象时并没有执⾏select语句,因为此时⼀级缓存也就是SqlSession缓存中已经缓存了id为1的Employee对象,Mybatis直接从缓存中将对象取出来,并没有再次去查询数据库,所以第⼆次也就没有执⾏select语句执⾏⽅法类⼆:public static void main(String[] args) {OneLevelCacheTest t = new OneLevelCacheTest();t.testCache2();}运⾏之后的结果:2018-08-22 09:41:17,024 DEBUG [com.gm.mapper.EmployeeMapper.selectEmployeeById] - ==> Preparing: SELECT * FROM employee WHERE id = ?2018-08-22 09:41:17,045 DEBUG [com.gm.mapper.EmployeeMapper.selectEmployeeById] - ==> Parameters: 1(Integer)2018-08-22 09:41:17,058 DEBUG [com.gm.mapper.EmployeeMapper.selectEmployeeById] - <== Total: 1com.gm.domain.Employee@56235b8e2018-08-22 09:41:17,058 DEBUG [com.gm.mapper.EmployeeMapper.deleteEmployeeById] - ==> Preparing: DELETE FROM employee WHERE id = ?2018-08-22 09:41:17,059 DEBUG [com.gm.mapper.EmployeeMapper.deleteEmployeeById] - ==> Parameters: 4(Integer)2018-08-22 09:41:17,079 DEBUG [com.gm.mapper.EmployeeMapper.deleteEmployeeById] - <== Updates: 12018-08-22 09:41:17,125 DEBUG [com.gm.mapper.EmployeeMapper.selectEmployeeById] - ==> Preparing: SELECT * FROM employee WHERE id = ?2018-08-22 09:41:17,125 DEBUG [com.gm.mapper.EmployeeMapper.selectEmployeeById] - ==> Parameters: 1(Integer)2018-08-22 09:41:17,128 DEBUG [com.gm.mapper.EmployeeMapper.selectEmployeeById] - <== Total: 1com.gm.domain.Employee@370736d9结果分析:在第⼀次查询id为1的employee对象时执⾏了⼀条select语句,接下来执⾏了⼀个delete并commit操作,Mybatis为了保证缓存中存储的是最新消息,会清空SqlSession缓存。
MyBatis二级缓存的用法什么是MyBatis二级缓存?在理解MyBatis二级缓存之前,我们先来了解一下MyBatis的一级缓存。
MyBatis的一级缓存是指SqlSession级别的缓存,它默认开启,并且是线程私有的。
也就是说,在同一个SqlSession中执行相同的SQL语句,第一次查询会将查询结果放入缓存中,后续再执行相同的SQL语句时,直接从缓存中获取结果,而不需要再去数据库查询。
而MyBatis的二级缓存则是SqlSessionFactory级别的缓存,它可以被多个SqlSession共享。
当多个SqlSession共享同一个SqlSessionFactory时,如果其中一个SqlSession执行了查询操作并将结果放入了二级缓存中,在后续其他SqlSession执行相同的查询操作时,可以直接从二级缓存中获取结果。
如何配置MyBatis二级缓存?要使用MyBatis二级缓存,我们需要进行以下几个步骤:1.在MyBatis配置文件(例如mybatis-config.xml)中开启二级缓存:<configuration><settings><setting name="cacheEnabled" value="true"/></settings></configuration>2.在需要使用二级缓存的Mapper接口上添加@CacheNamespace注解:@CacheNamespacepublic interface UserMapper {// ...}3.在需要进行缓存的查询语句上添加@Options注解,设置useCache属性为true:@Select("SELECT * FROM user WHERE id = #{id}")@Options(useCache = true)User getUserById(Long id);经过以上配置,我们就可以开始使用MyBatis的二级缓存了。
mybatis一级缓存和二级缓存原理
MyBatis一级缓存和二级缓存是MyBatis框架提供的两种缓存机制,用于提高数据读取性能。
一级缓存是指在MyBatis中SqlSession级别的缓存,它是默认开启的。
当SqlSession执行某个查询语句时,查询结果会被缓存到一级缓存中。
当再次执行同样的查询语句时,MyBatis会先从一级缓存中查找结果,如果存在,则直接返回缓存中的结果,否则再去数据库中查询。
一级缓存的生命周期与SqlSession的生命周期一致,只有在SqlSession关闭或清空缓存时才会失效。
二级缓存是指在MyBatis中全局范围的缓存,它是需要手动配置的。
当SqlSession执行某个查询语句时,查询结果会被缓存到二级缓存中。
当再次执行同样的查询语句时,MyBatis会先从二级缓存中查找结果,如果存在,则直接返回缓存中的结果,否则再去数据库中查询。
二级缓存的生命周期与应用程序的生命周期一致,只要应用程序没有关闭,二级缓存就会一直存在。
需要注意的是,一级缓存和二级缓存并不是完全相同的机制。
一级缓存是在SqlSession级别的缓存,而二级缓存是在全局范围的缓存。
一级缓存是默认开启的,而二级缓存需要手动配置。
另外,一级缓存只能用于同一个SqlSession中的查询,而二级缓存可以跨SqlSession共享缓存。
不过,由于二级缓存是全局缓存,所以需要考虑并发访问的问题,仅在数据更新不频繁且数据量较小的情况下才适用。
mybatis的缓存机制(一级缓存二级缓存和刷新缓存)和mybatis整合ehcache1 查询缓存1.1 什么是查询缓存mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。
mybaits 提供一级缓存,和二级缓存。
一级缓存是SqlSession级别的缓存。
在操作数据库时需要构造sqlSession对象,在对象中有一个(内存区域)数据结构(HashMap)用于存储缓存数据。
不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。
当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。
Mybatis默认开启一级缓存。
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper 的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
二级缓存是多个SqlSession 共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql 中传递参数也相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。
Mybatis默认没有开启二级缓存需要在setting全局参数中配置开启二级缓存。
如果缓存中有数据就不用从数据库中获取,大大提高系统性能。
1.2 一级缓存1.2.1 一级缓存工作原理下图是根据id查询用户的一级缓存图解第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。
ssm框架考试题和答案**SSM框架考试题和答案**一、单项选择题(每题2分,共20分)1. SSM框架中,S代表的是()。
A. SpringB. StrutsC. Spring MVCD. MyBatis答案:A2. 在Spring框架中,以下哪个不是Spring的核心功能?()A. 依赖注入B. 事务管理C. MVC框架D. AOP答案:C3. MyBatis中,以下哪个不是MyBatis的配置文件?()A. mybatis-config.xmlB. mybatis-spring.xmlC. log4j.propertiesD. applicationContext.xml答案:D4. 在Spring MVC中,以下哪个注解用于定义控制器类?()A. @ControllerB. @ServiceC. @RepositoryD. @Component答案:A5. 关于Struts2框架,以下说法错误的是()。
A. Struts2是一个基于MVC设计模式的Web框架B. Struts2使用OGNL作为表达式语言C. Struts2的配置文件是struts.xmlD. Struts2支持注解方式配置答案:D6. 在MyBatis中,以下哪个不是MyBatis的动态SQL?()A. ifB. chooseC. whereD. foreach答案:C7. Spring框架中,以下哪个不是事务管理的隔离级别?()A. READ_UNCOMMITTEDB. READ_COMMITTEDC. REPEATABLE_READD. TRANSACTION_SERIALIZABLE答案:A8. 在Spring MVC中,以下哪个注解用于定义请求映射?()A. @RequestMappingB. @PostMappingC. @GetMappingD. @PutMapping答案:A9. MyBatis中,以下哪个不是MyBatis的插件接口?()A. InterceptorB. TypeHandlerC. ExecutorD. Mapper答案:D10. 在Spring框架中,以下哪个不是Spring提供的AOP实现方式?()A. JDK动态代理B. CGLIB代理C. AspectJD. Spring AOP答案:D二、多项选择题(每题3分,共15分)11. SSM框架中,以下哪些是Spring框架的优点?()A. 轻量级B. 控制反转C. 面向切面编程D. 事务管理答案:ABCD12. 在MyBatis中,以下哪些是MyBatis支持的配置元素?()A. propertiesB. settingsC. typeAliasesD. mappers答案:ABCD13. 在Spring MVC中,以下哪些是Spring MVC提供的注解?()A. @RequestParamB. @PathVariableC. @RequestBodyD. @ResponseBody答案:ABCD14. 在Spring框架中,以下哪些是Spring支持的事务管理方式?()A. 编程式事务管理B. 声明式事务管理C. 基于AOP的声明式事务管理D. 基于注解的声明式事务管理答案:ABCD15. 在MyBatis中,以下哪些是MyBatis支持的缓存机制?()A. 一级缓存B. 二级缓存C. 三级缓存D. 四级缓存答案:AB三、简答题(每题10分,共30分)16. 请简述SSM框架的组成及其作用。
Java后端程序员3年工作经验总结(一)工作已经3年有余,这3年里特别感谢技术管理人员的器重,以及同事的帮忙,学到了不少东西。
这3年里走过一些弯路,也碰到一些难题,也受到过做为一名开发却经常为系统维护和发布当救火队员的苦恼。
遂决定梳理一下自己所学的东西,为大家分享一下。
经过3年意识到以前也有很多认识误区,比如:偏爱收集,经常收集各种资料视频塞满一个个硬盘,然后心满意足的看着容量不行动。
不重基础,总觉得很多基础东西不需要再看了,其实不懂的地方很多,计算机程序方面任何一个结果都必有原因,不要只会用不知道原理,那是加工厂出来的。
现在ide查看代码那么方便,ctrl+点击就进入了JDK查看实现细节。
好有野心,在计算机基础不扎实的时候,总想做架构、分发、大数据之类的。
不重视性能,只求能实现功能,sql查询是不是可以优化,是否有算法妙用,大对象是否要清除。
不重视扩展性,模块之间紧密耦合,常用方法不提取成工具类,调用关系混乱等问题。
……本文不关注这些,所以只列出一小部分。
让我们言归正传。
2.语法基础2.1 Java类初始化顺序这是所有情况的类初始化顺序,如果实际类中没有定义则跳过:父类静态变量——父类静态代码块——子类静态代码块——父类非静态变量——父类非静态代码块——父类构造函数——子类非静态变量——子类非静态代码块——子类构造函数2.2 值传递和引用传递可能很多人对此不屑一顾,心想老子都工作3年了,对这些还不熟悉吗?但实际情况并非这样,JDK中东西全部熟悉了吗?以一个最简单的例子开始,你觉得下图中代码执行完之后fatherList中的元素是什么?这是一个最基础的值传递和引用传递的例子,你觉得好简单,已经想跃跃欲试的挑战了,那么请看下面的,StringBuffer很好理解,但是当你执行一遍之后发现是不是和预想中的输出不一样呢?String不是引用类型吗,怎么会这样呢?如果你无法理解,那么请看下String的实现源码,了解下其在内存中分配的实现原理。
Mybatis的⼀级缓存和⼆级缓存的理解以及⽤法 程序中为什么使⽤缓存? 先了解⼀下缓存的概念:原始意义是指访问速度⽐⼀般随机存取存储器快的⼀种RAM,通常它不像系统主存那样使⽤DRAM技术,⽽使⽤昂贵但较快速的SRAM技术。
对于我们编程来说,所谓的缓存,就是将程序或系统经常要调⽤的对象(临时数据)存在内存中,⼀遍其使⽤时可以快速调⽤,不必再去创建新的重复的实例。
这样做可以减少系统的开销,提⾼效率。
对缓存有了⼀定的了解以后就知道了使⽤缓存是为了减少和数据库的交互次数,提⾼执⾏效率。
那么下⼀个问题来了。
什么样的数据能使⽤缓存,什么样的数据不能使⽤? 这是我们使⽤缓存必须要明确的事情,实际上适⽤于缓存的数据:经常查询并且不经常改变的,并且的数据的正确与否对最终结果影响不⼤的、不适⽤于缓存的数据:经常改变的数据,数据的正确与否对最终结果影响很⼤的。
Mybatis中的⼀级缓存和⼆级缓存到底缓存了什么,缓存了以后⼜有什么效果,缓存的数据什么时候会被清空? ⼀级缓存:它指的是Mybatis中sqlSession对象的缓存,当我们执⾏查询以后,查询的结果会同时存⼊到SqlSession为我们提供的⼀块区域中,该区域的结构是⼀个Map,当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,的话直接拿出来⽤,当SqlSession对象消失时,mybatis的⼀级缓存也就消失了,同时⼀级缓存是SqlSession范围的缓存,当调⽤SqlSession的修改、添加、删除、commit(),close等⽅法时,就会清空⼀级缓存。
⼆级缓存:他值得是Mybatis中SqlSessionFactory对象的缓存,由同⼀个SqlSessionFactory对象创建的SqlSession共享其缓存,但是其中缓存的是数据⽽不是对象,所以从⼆级缓存再次查询出得结果的对象与第⼀次存⼊的对象是不⼀样的。
通过简单的例⼦来加深理解⼀级缓存和⼆级缓存。
mybatis中的一二级缓存的实现原理
Mybatis是一款非常流行的Java持久层框架,它采用了缓存机制来提高数据库交互的效率。
其中一级缓存和二级缓存是两种不同的缓存机制,它们都有自己的实现原理。
一级缓存是指在同一个SqlSession中执行相同的SQL语句,返回的结果会被缓存起来。
在同一个SqlSession中,如果执行了相同的SQL语句,Mybatis会先从缓存中查找结果,如果有则直接返回,如果没有则执行SQL语句并将结果缓存。
一级缓存的实现原理是通过一个HashMap来存储结果,其中key为SQL语句,value为结果集。
二级缓存是指在同一个Mapper中执行相同的SQL语句,返回的结果会被缓存起来。
二级缓存是跨SqlSession的,也就是说在不同的SqlSession中执行相同的SQL语句,返回的结果会先从二级缓存中查找。
二级缓存的实现原理是通过一个Cache接口来实现的,该接口有多种实现方式,比如Ehcache、Redis等。
其中,Mybatis默认使用PerpetualCache作为二级缓存的实现方式。
需要注意的是,一级缓存和二级缓存并不是完全相同的,它们有不同的适用场景。
一级缓存适用于单条数据查询,而二级缓存适用于多条数据查询。
此外,在使用二级缓存时需要注意缓存的有效期,避免出现脏数据问题。
总之, Mybatis的缓存机制是通过一级缓存和二级缓存来提高数据库交互效率的。
对于开发者来说,需要根据业务需求选择合适的缓存机制,避免出现性能问题。
MyBatis 是一款流行的 Java 持久层框架,在处理数据库操作时具有高效性和灵活性。
为了提升查询性能,MyBatis 提供了缓存机制,可以将查询结果缓存起来并复用,减少对数据库的访问次数,从而提高系统的响应速度。
本文将介绍 MyBatis 缓存机制的原理和使用方法。
一、MyBatis 缓存机制的原理MyBatis 缓存机制是基于对象的缓存,它将查询结果缓存在内存中,以避免重复查询数据库。
MyBatis 的缓存机制主要由以下几部分组成:一级缓存、二级缓存和本地缓存。
1. 一级缓存:一级缓存是指在同一个 SqlSession 中,对于相同的查询语句和参数,MyBatis 会将查询结果缓存在内存中。
这样,如果后续再次执行相同的查询,MyBatis 会直接从缓存中获取结果,而不需要再次查询数据库。
一级缓存的生命周期与 SqlSession 相同,当 SqlSession 调用 commit()、close()、clearCache() 等方法时,一级缓存会被清空。
此外,如果在同一个SqlSession 中进行了更新、插入或删除操作,可能会导致一级缓存失效,即查询结果不会被缓存。
2. 二级缓存:二级缓存是指在不同的 SqlSession 之间共享缓存。
当多个 SqlSession 执行相同的查询,并且开启了二级缓存配置,MyBatis 会将查询结果缓存在二级缓存中。
这样,其他SqlSession 执行相同的查询时,可以直接从二级缓存中获取结果,而无需再次查询数据库。
二级缓存的作用域是 Mapper 级别的,即同一个 Mapper 接口中的方法共享缓存。
要启用二级缓存,需要在配置文件中配置<cache /> 标签,并在Mapper 接口中添加@CacheNamespace 注解。
3. 本地缓存:本地缓存是指 MyBatis 在执行查询时,会将查询结果缓存在 Statement 对象中。
如果后续再次执行相同的查询,MyBatis 会直接从 Statement 对象中获取结果,而无需再次查询数据库。
SpringBoot整合mybatis常见问题(⼩结)Spring中常见问题1.NoSuchBeanDefinitionException2.'..Service' that could not be found service找不到3.port 80 was already in use 端⼝号被占⽤4.TemplateInputException 模板解析异常或找不到模板1.检查模板所在的⽬录是否与配置的前缀⽬录相同2.检查返回的模板是否存在,返回值类型是否⼀致3.检查配置前缀时是否以"/"斜杠结尾4.控制层的url与客户端的ur是否⼀致5. 404异常访问资源不存在6. 500异常 500异常要查看控制台Mybatis中常见问题1.springboot中添加maven依赖2.BadSqlGrammarException 错误的sql语句3.BindingException 绑定异常1.检查映射⽂件的路径配置与实际存储位置是否⼀致2.检查dao接⼝的类名是否与映射⽂件的namespace值相同(不能有空格)3.检查dao接⼝中的⽅法名是否在映射⽂件中有对应的id4.IllegalArgumentException原因:同样说我sql映射是否出现了重复性的定义(例如:分别以注解⽅式和xml配置⽂件⽅式进⾏定义,也就是说在同⼀个namespace下出现了重复的元素id)5.SAXParseException xml解析问题补充问题⼀:Mapper类 autowired失败原因:扫描mapper包没有配置或配置不正确解决:⽅案⼀:1. 启动类加@MapperScan("mapperPackagePath")⽅案⼆:增加配置类:package com.yx.readingwebsite.config;import org.mybatis.spring.mapper.MapperScannerConfigurer;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/*** MapperScannerConfigurer 配置DAO层*/@Configurationpublic class MyBatisMapperScannerConfig {@Beanpublic MapperScannerConfigurer getMapperScannerConfigurer(){MapperScannerConfigurer msc = new MapperScannerConfigurer();msc.setSqlSessionFactoryBeanName("sqlSessionFactory");msc.setBasePackage("com.yx.readingwebsite.mapper");return msc;}}问题⼆:Mapper扫描成功后,继续报错,org.apache.ibatis.binding.BindingException: Invalid bound statement (not found):原因:xml的mapper SQL 和 Mapper接⼝没有绑定解决:⽅案⼀:全局配置⽂件application.yml增加mybatis配置【xml mapper包在resource⽬录下】mybatis:mapper-locations: classpath:mapper/*.xml⽅案⼆:增加配置类package com.yx.readingwebsite.config;import org.apache.ibatis.session.SqlSessionFactory;import org.mybatis.spring.SqlSessionFactoryBean;import org.mybatis.spring.SqlSessionTemplate;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.io.Resource;import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import org.springframework.jdbc.datasource.DataSourceTransactionManager;import org.springframework.transaction.PlatformTransactionManager;import org.springframework.transaction.annotation.EnableTransactionManagement;import org.springframework.transaction.annotation.TransactionManagementConfigurer;import javax.sql.DataSource;/*** 配置MyBatis,引⼊数据源,sqlSessionFactory,sqlSessionTemplate,事务管理器*/@Configuration //配置类@EnableTransactionManagement //允许使⽤事务管理器public class MyBatisModelConfig implements TransactionManagementConfigurer {@Autowiredprivate DataSource dataSource;@Bean(name = "sqlSessionFactory")public SqlSessionFactory getSqlSessionFactory(){SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();ssfb.setDataSource(dataSource); //设置数据源ssfb.setTypeAliasesPackage("com.yx.readingwebsite.model"); //设置扫描模型包【po】try {Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml");ssfb.setMapperLocations(resources);return ssfb.getObject();} catch (Exception e) {e.printStackTrace();throw new RuntimeException();}}@Bean //获得Session 模板,从⽽获得Sessionpublic SqlSessionTemplate getSqlSessionTemplate(SqlSessionFactory sqlSessionFactory){return new SqlSessionTemplate(sqlSessionFactory);}@Override //事务管理器public PlatformTransactionManager annotationDrivenTransactionManager() {return new DataSourceTransactionManager(dataSource);}}需要注意的是,xml版的mybatis⼀定要在sqlSessionFactory中指定mapperLocations,即下图总结:两种配置⽅案。
MyBatis经典⾯试题及答案1、什么是MyBatis?答:MyBatis是⼀个可以⾃定义SQL、存储过程和⾼级映射的持久层框架。
2、讲下MyBatis的缓存答:MyBatis的缓存分为⼀级缓存和⼆级缓存,⼀级缓存放在session⾥⾯,默认就有,⼆级缓存放在它的命名空间⾥,默认是不打开的,使⽤⼆级缓存属性类需要实现Serializable序列化接⼝(可⽤来保存对象的状态),可在它的映射⽂件中配置<cache/>1、⼀级缓存的⽣命周期有多长?a、MyBatis在开启⼀个数据库会话时,会创建⼀个新的SqlSession对象,SqlSession对象中会有⼀个新的Executor对象。
Executor对象中持有⼀个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也⼀并释放掉。
b、如果SqlSession调⽤了close()⽅法,会释放掉⼀级缓存PerpetualCache对象,⼀级缓存将不可⽤。
c、如果SqlSession调⽤了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使⽤。
d、SqlSession中执⾏了任何⼀个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使⽤2、怎么判断某两次查询是完全相同的查询?mybatis认为,对于两次查询,如果以下条件都完全⼀样,那么就认为它们是完全相同的两次查询。
2.1 传⼊的statementId2.2 查询时要求的结果集中的结果范围2.3. 这次查询所产⽣的最终要传递给JDBC java.sql.Preparedstatement的Sql语句字符串(boundSql.getSql() )2.4 传递给java.sql.Statement要设置的参数值如果我们配置了⼆级缓存就意味着:映射语句⽂件中的所有select语句将会被缓存。
mybatis 二级缓存 cache 参数MyBatis二级缓存参数详解一、概述MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。
MyBatis提供了两种类型的缓存:一级缓存和二级缓存。
其中,一级缓存在MyBatis内部实现,而二级缓存则是由用户自定义实现的。
二级缓存通常用于提高查询效率,减少数据库的访问压力。
本篇文章将详细介绍MyBatis二级缓存的参数设置。
二、缓存参数设置MyBatis的二级缓存可以通过配置文件进行设置。
以下是一些常用的缓存参数:1. <property name="cacheEnabled" value="true|false" />该属性用于启用或禁用二级缓存。
默认值为true,表示启用二级缓存。
如果设置为false,则不会使用二级缓存,所有的查询操作都会直接发送到数据库。
2. <property name="cacheKeyGenerator" value="自定义的KeyGenerator" />该属性用于指定用于生成缓存键的KeyGenerator。
MyBatis提供了默认的KeyGenerator,但有时可能需要自定义KeyGenerator以满足特定的需求。
3. <property name="cacheEvictionPolicy" value="自定义的EvictionPolicy" />该属性用于指定缓存的驱逐策略。
MyBatis提供了几种默认的EvictionPolicy,如FIFO(先进先出)和LFU(最少使用)。
如果需要使用自定义的EvictionPolicy,则需要实现EvictionPolicy接口。
4. <property name="cacheCloner" value="true|false" />该属性用于启用或禁用二级缓存的克隆功能。
一、默认开启一级缓存。
一级缓存是 SqlSession 级别的。
具体什么意思测试一下。
一次事务中,同一语句调用两次,代码:
VarDateEntity varDateEntity = dateMapper.getVarDate();
System.out.println("var1 var:"+varDateEntity.getVarTime());
System.out.println("var1 not:"+varDateEntity.getNotTime());
System.out.println("var1 null:"+varDateEntity.getNullTime());
varDateEntity = dateMapper.getVarDate();
System.out.println("var2 var:"+varDateEntity.getVarTime());
System.out.println("var2 not:"+varDateEntity.getNotTime());
System.out.println("var2 null:"+varDateEntity.getNullTime());
Postman 中执行两次:
测试结果:
结论1:
同一事务中的两次查询,只查询了一次。
但是两次事务中,每次均进行了一次查询;
一级缓存的 scope 默认值session;
设置为:statment 。
重启 springboot 应用再次测试:
结论2:
设置mybatis.configuration.local-cache-scope=statement后,即使 xmxxxxl 语句中配置useCache="true",一级缓存均失效。
总结:
•Mybatis一级缓存的生命周期和SqlSession一致。
•Mybatis的缓存是一个粗粒度的缓存,没有更新缓存和缓存过期的概念,同时只是使用了默认的hashmap,也没有做容量上的限定。
•Mybatis的一级缓存最大范围是SqlSession内部,有多个SqlSession或者分布式的环境下,有操作数据库写的话,会引起脏数据,建议是把一级缓存的默认级别设定为
Statement,即不使用一级缓存。
二、开启二级缓存,需要进行设置。
#一级缓存状态:关闭 #
mybatis.configuration.local-cache-scope=statement
#二级缓存状态:开启 #
mybatis.configuration.cache-enabled=true
mapper定义中添加标签<cache />或<cache-ref />。
<cache />
cache 用来声明使用二级缓存,可以自定义属性:
•type: cache使用的类型,默认是PerpetualCache,这在一级缓存中提到过。
•eviction: 定义回收的策略,常见的有FIFO,默认LRU
•flushInterval: 配置一定时间自动刷新缓存,单位是毫秒
•size: 最多缓存对象的个数
•readOnly: 是否只读,若配置可读写,则需要对应的实体类能够序列化。
•blocking: 若缓存中找不到对应的key,是否会一直blocking,直到有对应的数据进入缓存。
cache-ref代表引用别的命名空间配置,使用同一个 Cache。
<cache-ref namespace="com.xxx.table_name"/>
测试<cache />:重启应用报错:
Caused by: java.io.NotSerializableException: com.sitech.acctmgr.pub.entity.VarDateEntity
没有序列化?将VarDateEntity序列化。
public class VarDateEntity implements Serializable {
重启应用,Postman第一次SqlSession 事务调用中,两次相同的查询:
两次均调用 sql查询了数据库。
原因:同一个 SqlSession 中,在执行完毕后进行提交commit()才会进入缓存。
Postman第二次事务执行,调用相同的 sql:
使用了缓存。
命中率:Cache Hit Ratio : 0.3333333333333333,为何是0.3,因为总共查询了3次,本次使用了缓存。
再次查询,命中率就会变为0.5:
总结:
二级缓存不同于一级缓存,需要使接受数据的 POJO类序列化;
同一事务中的查询信息,在事务提交之后,才会把查询数据提交缓存。
返回的缓存数据是实际缓存实例,不是拷贝,所以存在被修改的情况!!!
频繁更新的操作不建议使用。
手动修改数据库表中的查询数据会出现脏读!!!
二级缓存对细粒度的数据级别的缓存实现不好,不能够单独刷新某条数据!!!方案?EhcacheCache。
以上是在 SpringBoot 中,对Mybatis的缓存的查询类测试。
关于更新类的测试结果总结:
update操作是否会刷新该namespace下的二级缓存;
Mybatis的二级缓存不适应用于映射文件中存在多表查询的情况。
如何解决?使用cache-ref 引入其他关联表所载 namespace。
单应用方案:
二级缓存?业务第一次的时候做的缓存。
单独做缓存?应用启动的时候静态加载配置。
业务无感知。
分布式方案:
分布式缓存redis?
使用内存数据库?
mybatis源码分析可以查看下附帖子,在很多搜索结果里面,是比较全面的。