本篇博客为MyBatis的后篇学习,关于前篇部分的学习通过下面链接进行学习吧。
SSM框架—MyBatis学习前篇学习连接:SSM框架—MyBatis学习前篇

关于本篇博客目录:

  • 七.注解开发
    • 7.1.在Mybatis中使用注解开发
    • 7.2 SQL注解
      • 7.2.1.查询@Select
      • 7.2.2 增加@Insert
      • 7.2.3 删除@Delete
      • 7.2.4 修改@Update
      • 7.2.5 实体类和数据库字段名不一致问题
    • 7.3 @Param()注解
      • 7.3.1 关于@Param()
    • 7.4 LomBok
      • 7.4.1 为什么使用LomBok
      • 7.4.2 LomBok在IDEA中使用
      • 7.4.3 常用注解说明
  • 八. XML映射文件
    • 8.1第四章补充内容ResultMap
      • 8.1.1 多对一
      • 8.1.2 一对多
  • 九 动态SQL
    • 9.1 什么是动态SQL
    • 9.2 在项目中使用动态SQL
      • 9.2.1 if
      • 9.2.2 where
      • 9.2.3 set
      • 9.2.4 choose、when、otherwise
      • 9.2.5 trim
      • 9.2.6 foreach
  • 十. 缓存
    • 10.1 什么是缓存
    • 10.2 一级缓存
      • 10.2.1 一级缓存测试
    • 10.3 二级缓存
      • 10.3.1 二级缓存测试
      • 10.3.2 自定义缓存Ehcache

七.注解开发

7.1.在Mybatis中使用注解开发

在前面说到在映射语句时可以不使用Mapper.xml方式来映射语句,可以使用注解方式实现,这一点在官方文档中也提到,如下点击跳转官方文档查看:

对于要使用注解还是xml形式官方文档说得非常明确:使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句
选择何种方式来配置映射,以及认为是否应该要统一映射语句定义的形式,完全取决于你和你的团队。 换句话说,永远不要拘泥于一种方式,你可以很轻松的在基于注解和 XML 的语句映射方式间自由移植和切换。

7.2 SQL注解

SQL中CRUD对应注解:

@Select(这里面写Sql语句)  查询注解
@Update(这里面写Sql语句)  修改注解
@Delete(这里面写Sql语句)  删除注解
@Insert(这里面写Sql语句)  增加注解

7.2.1.查询@Select

业务1.使用注解查询数据库中所有用户信息
步骤1:在接口中编写查询数据的方法,并在方法上使用注解
在步骤1这里,使用注解后就可以省略以前步骤2的操作,在对应xml中写映射语句

步骤2:在DemoTest中编写测试方法
这里的操作和以前没有任何变化

运行结果:

注意:在mybatis-config.xml的配置文件中Mappers的配置只需要找到该接口即可,如果以前为找到对应的.xml的话,需要改过来.

改为以下二种任意一种:

7.2.2 增加@Insert

步骤1:在接口中编写增加数据的方法,并在方法上使用注解

步骤2:写测试方法

运行结果:

7.2.3 删除@Delete

步骤1:在接口中编写删除数据的方法,并在方法上使用注解

步骤2:写测试方法

运行结果:

7.2.4 修改@Update

步骤1:在接口中编写修改数据的方法,并在方法上使用注解

步骤2:写测试方法

运行结果:

7.2.5 实体类和数据库字段名不一致问题

执行selectUserAll_annotation()我们会发现查询出来的数据password=0:

对于这个问题在前篇中造成的原因: 实体类和数据库字段名不一致问题
对于这个问题ResultMap就无法使用,这里就只能通过取别名的方式进行解决,所以在注解Sql语句时候要考虑这一个因素。
这里给pwd取别名:

再在测试类中执行selectUserAll_annotation()【问题解决】:

7.3 @Param()注解

7.3.1 关于@Param()

@Param就是给方法的形式参数取名
注意:

  • 基本类型的参数或者String类型,需要加上@Param()
  • 引用数据类型不需要加
  • 如果只有一个基本数据类型的话,可以忽略,但是建议加上
  • 我们在SQL中引用的就是我们这里的@Param()中设定的属性名
  • 方法存在多个参数,每一个参数必须加上

就像这样进行使用:

1. 关于最后一条(我们在SQL中引用的就是我们这里的@Param()中设定的属性名)进行解释:
步骤1.现在测试类中执行addUserTest_annotation()
此时数据如下,多了id=6的数据

接口中的删除方法:

步骤2.在测试类中执行delUserTest_annotation()进行测试:
执行后会报错,这是因为@Param(“id2”) 这里参数为id2而Sql语句中id=#{id} 这里是id,,所以这里出现id没有找到。

将@Param(“id2”) 这里的id2改为id即可:

再在测试类中执行delUserTest_annotation()进行测试:

7.4 LomBok

7.4.1 为什么使用LomBok

LomBok是一个插件,可以大大的简化Java代码,特别是在实体类中,可以通过相应的注解来代替Get,Set,有参,无参构造器,toString()等实现代码,但是LomBok改变了写源码的方式所以在使用的时候注意取舍。

7.4.2 LomBok在IDEA中使用

步骤1:安装LomBok插件


步骤2:导入LomBok的Maven

  <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.8</version></dependency>


步骤3:在实体类中使用LomBok
关于实体类中的User类

使用注解:

从上面的注解方式的使用可以看出,使用注解可以少写很多代码。

7.4.3 常用注解说明

  • @Data

注解在类上。在JavaBean或类JavaBean中使用,这个注解包含范围最广,它包含getter、setter、NoArgsConstructor、equals、canEqual、hashCode、toString注解,即当使用注解时,会自动生成包含的所有方法;

  • @Setter
  • 注解在类上。使用此注解会生成对应的setter方法。
  • Getter

注解在类上。使用此注解会生成对应的getter方法。

  • @NoArgsConstructor

注解在类上。使用此注解会生成对应的无参构造方法。

  • @AllArgsConstructor

注解在类上。使用此注解会生成对应的有参构造方法。

  • @ToString

注解在类上。使用此注解会自动重写对应的toStirng方法。

  • @EqualsAndHashCode

注解在类上。使用此注解会自动重写对应的equals方法和hashCode方法。

  • @Slf4j

注解在类上。当项目中使用了slf4j打印日志框架时使用该注解,会简化日志的打印流程,只需调用info方法即可;

  • @Log4j

注解在类上。当项目中使用了log4j打印日志框架时使用该注解,会简化日志的打印流程,只需调用info方法即可。

  • val

用在局部变量前面,相当于将变量声明为final。

  • @NonNull

给方法参数增加这个注解会自动在方法内对该参数进行是否为空的校验,如果为空,则抛出NPE(NullPointerException)

  • @Cleanup

自动管理资源,用在局部变量之前,在当前变量范围内即将执行完毕退出之前会自动清理资源,自动生成try-finally这样的代码来关闭流

  • @Value

用在类上,是@Data的不可变形式,相当于为属性添加final声明,只提供getter方法,而不提供setter方法

  • @Builder

用在类、构造器、方法上,为你提供复杂的builder APIs,让你可以像如下方式一样调用Person.builder().name(“Adam Savage”).city(“San Francisco”).job(“Mythbusters”).job(“Unchained Reaction”).build();

  • @SneakyThrows

自动抛受检异常,而无需显式在方法上使用throws语句

  • @Synchronized

用在方法上,将方法声明为同步的,并自动加锁,而锁对象是一个私有的属性lock或lock或lock或LOCK,而java中的synchronized关键字锁对象是this,锁在this或者自己的类对象上存在副作用,就是你不能阻止非受控代码去锁this或者类对象,这可能会导致竞争条件或者其它线程错误

关于LomBok更多的讲解,可以观看此篇博文,非常详细博客园-日拱一兵-随笔 -Lombok 使用详解,简化Java编程

八. XML映射文件

8.1第四章补充内容ResultMap

详细信息查看官方文档:点击跳转到官方文档

8.1.1 多对一

多对一的关系:在一个班中有多名学生,但是他们只有一位语文老师,这种多位学生对应一位老师的关系叫做多对一。
业务1:查询所有学生信息,包括对应的老师信息。
步骤1:设计数据库,并在项目中pojo中编写实体类

CREATE TABLE `teacher` (`id` INT(10) NOT NULL,`name` VARCHAR(30) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;INSERT INTO teacher(`id`, `name`) VALUES (1, 秦老师); CREATE TABLE `student` (`id` INT(10) NOT NULL,`name` VARCHAR(30) DEFAULT NULL,`tid` INT(10) DEFAULT NULL,PRIMARY KEY (`id`),KEY `fktid` (`tid`),CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (1, 小明, 1);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (2, 小红, 1);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (3, 小张, 1);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (4, 小李, 1);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (5, 小王, 1);


步骤2:添加Mapper接口和对应xml文件

步骤3:编写对应的映射语句
这里有二种方式可以实现:

  • 实现方式:按结果集进行查询

就是单纯的编写完整的SQL语句,别名问题就用ResultMap解决,ResultMap中对象用association,集合用collection。
完整SQ语句L:

  select s.id as sid,s.name as sname,s.tid as stid,t.name as tnamefrom student s,teacher twhere s.tid=t.id;

这里:Javatype:全限定名就是实体类Teacher


javaType=“Teacher” 这里其实是省略了的完整应该是 javaType=" pojo.Teacher"

  • 通过子查询方式进行获取

    步骤3:编写对应的测试方法
    按结果集进行查询:

按子查询方式进行查询:

运行结果:

8.1.2 一对多

一对多关系:一个班里多名学生,但是只有一个语文老师,一个语文老师对应多个学生,这种关系就是一对多关系。
业务2:查询指定老师信息,并查询出对应的学生
步骤1:编写Mapper接口和相应xml

老师的实体信息:

步骤2:编写相应的映射语句

  • 按结果集进行查询

  • 按子查询方式进行查询

步骤3:编写对应的测试方法
按结果集进行查询:

按子查询方式进行查询:

运行结果:

九 动态SQL

9.1 什么是动态SQL

详细信息参照官方文档:点击跳转官方文档查看详细信息
根据相应条件生成对应的SQL语句,相对于以前的纯Java代码中,就是通过相应的If判断进行SQL语句的拼接,这就是动态SQL。
在Mybatis中用于动态SQL的标签:

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

9.2 在项目中使用动态SQL

9.2.1 if

在前面的项目中,有一个selectUserAll方法查找所有用户信息,这里我们通过用户提供的参数情况进行动态查找

在UserMapper接口中添加一个selectUserAll_2(Map<String,Object> map)的方法

这里增加了二个条件判断,通过二个条件判断可以动态的实现数据查找

在测试类中进行测试:
这样进行传参就实现了前面的selectUserCondition()方法

二者的运行结果是一致的,都是查找名字中包含张的用户:

如果不添加任何条件就是查询所有用户,实现了selectUserAll()方法的业务:

运行结果:

如果只传递id的话就是实现了selectUserById()方法:

运行结果:

9.2.2 where

在前面的if使用中有一个1=1其实是没有必要存在的,因为1=1是必定存在。
如果把where 1=1去掉,大概会修改为这样

但是这样的话,如果id=null的时候这时候的SQL语句就变成这样:select * from user_test And name like %传递的参数%,这样显然是错误的,这也是为什么加where 1=1的原因,为了解决这个问题,这里可以使用<where> 这样就可以当Where 后面第一个出现AndOr的时候自动的去掉,并且如果SQL语句后面没有条件的时候<where>就不会在对应的SQL后面添加where

当没有参数时候运行结果:

和前面有where 1=1的时候一样,但是和where 1=1相比这样更加智能,并且更符合逻辑:

9.2.3 set

在数据修改中有的时候,某列数据没有做修改但是我们在以前写的SQL语句,其实进行了修改。

比如上面这个SQL映射语句,当用户不修改密码,只修改姓名时,这里的密码是和数据库一致的,但是在执行SQL语句的时候还是对密码这一字段进行了修改,对于这种情况我们可以使用<set>
在接口中添加一个updateUser_2(Map<String,Object> map)方法

相应的SQL映射语句如下:

测试:

运行结果:

并且<set>对于后面遇见的第一个,会自动去掉:


9.2.4 choose、when、otherwise

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
在接口中添加一个selectUserAll_3(Map<String,Object> map)的方法

在接口类对应的映射语句

通过这个语句,可以看出,有哪一个参数不为空就查哪一个,如果都为空就查用户id=1的用户信息:
测试:

运行结果:

9.2.5 trim

在前面的<where><set>如果和你的期望不一样,你可以通过<trim>来自定义。
1.<trim> 中的参数解释:

  • prefix:前缀
  • prefixOverrides:前缀无效
  • suffix:后缀
  • suffixOverrides:后缀无效

这里的前后的意思就是<trim>中相应的映射语句的最前面和最后面

2.前面的<where>

通过<trim>来自定义:
prefix="where"如果<trim>中有要执行的SQL语句,就在该SQL语句最前面添加Where,相反没有就不添加,prefixOverrides="AND |OR"如果Where后面有AND或者OR就移除掉,相反没有就不移除。

测试:

运行结果和用<where>是一样的:

3.前面的<set>

通过<trim>来自定义:
prefix="set"如果<trim>中有要执行的SQL语句,就在该SQL语句最前面添加set,相反没有就不添加,suffixOverrides=","如果最后面中有,最后面的,就移除掉,相反没有就不移除。

测试:

运行结果和updateUserTest_2前面一致:

9.2.6 foreach

业务:查询用户Id包含1,3,5的用户信息
步骤1:在接口类中添加方法

步骤2:在对应接口类的xml中编写映射语句
open="("当传递的集合有值时,先加"("
close=")" 集合遍历完之后在最后面加")"
separator=","每遍历集合中的一个数后加一个","
collection="list"传递的集合
执行过后就是

select * from user_test where id in (1,3,5)


测试:

执行结果:

十. 缓存

10.1 什么是缓存

缓存就是一个临时存放数据的地方,没有使用缓存之前对于数据的操作(这里的操作主要是查询)基本上是直接和服务器进行交互,如果多次查询同一个数据,这样就十分消耗资源,为了针对这种情况,将查询过后的数据放在一个临时地方,当多次查询同一个数据的时候,第二次就无需再从服务器中进行数据查询,而是从这个临时存放数据的地方进行查询,为了保障数据的一致性,如果用户执行增删改等操作,缓存会进行刷新。
没有使用缓存情况:

使用缓存情况:

10.2 一级缓存

MyBatis缓存分为一级缓存和二级缓存,一级缓存和二级缓存最大的区别在于生命周期不同,一级缓存的生命周期是SqlSession对象的使用期间,随着SqlSession对象的死亡而消失,并且一级缓存可由SqlSession来手动清除。

10.2.1 一级缓存测试

在测试类中查询二次selectUserAll_2,传递参数相同:

可以看出,第二次查询的时候是没有从服务器中返回数据的,而是直接从缓存中返回数据

如果参数不同呢:

这时就没有从缓存中取到数据,而是在服务器中查询之后再返回查询结果:

如果执行二个不同的方法:

执行结果:

在二个查询相同的方法中间执行增|删|改操作,验证了前面说的当执行了增删改操作之后对应的缓存会刷新:

10.3 二级缓存

二级缓存又叫全局缓存,在一个nameSpace下有效,二级缓存需要手动配置。
1.<settings>中开启缓存,其实默认是开启的。

2.在映射语句xml中添加<cache>标签
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
可用的清除策略有:

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

flushInterval:(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
size:(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
readOnly:(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

<cacheeviction="FIFO"flushInterval="60000"size="512"readOnly="true"/>


二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。

10.3.1 二级缓存测试

使用一级缓存执行以下测试:

测试结果:

使用二级缓存进行测试:

运行结果:

Mybatis缓存查询顺序,如果二级缓存开启,先从二级缓存进行查找,如果二级缓存没有数据,就向一级缓存去查找,如果一级缓存没有数据就从数据库进行查找。

10.3.2 自定义缓存Ehcache

Java缓存框架 EhCache EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。
使用Ehcache:
1.导入Ehcache的依赖

   <dependency><groupId>org.mybatis.caches</groupId><artifactId>mybatis-ehcache</artifactId><version>1.0.3</version></dependency>

2.创建ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"><!-- 磁盘保存路径 --><diskStore path="F:/ehcache"/><defaultCacheeternal="false"maxElementsInMemory="10000"overflowToDisk="false"diskPersistent="false"timeToIdleSeconds="1800"timeToLiveSeconds="259200"memoryStoreEvictionPolicy="LRU"/><cachename="cloud_user"eternal="false"maxElementsInMemory="5000"overflowToDisk="false"diskPersistent="false"timeToIdleSeconds="1800"timeToLiveSeconds="1800"memoryStoreEvictionPolicy="LRU"/>
</ehcache><!--属性说明:l diskStore:当内存中不够存储时,存储到指定数据在磁盘中的存储位置。l defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略以下属性是必须的:l maxElementsInMemory - 在内存中缓存的element的最大数目l maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大l eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断l overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上以下属性是可选的:l timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大l timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.l diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。l diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作l memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)-->

3.在对应的mapper类中使用指定的缓存,而要使用第三方缓存则要实现Cache接口(此接口是Mybatis提供的)

使用自定义缓存实现一些测试:

实现结果:

其实运行效果和二级缓存,一级缓存没有任何区别,就是一种新的缓存框架,因为在实际开发中使用不多,这里只需要了解即可。
使用第三方缓存则要实现Cache接口:这里只需要实现Cache接口即可并且重写该接口的相应方法来实现对应的缓存操作,这里了解一下即可:


----------- 完结 -----------撒花 -----------撒花 -----------撒花 -----------撒花 -----------撒花 -----------撒花 -----------撒花 ----------- 撒花----------- —撒花
该博文存在什么问题?,或者你有什么想说的?欢迎留言区进行留言!

MyBatis学习后篇相关推荐

  1. 超详细Mybatis学习笔记(可供下载)

    文章目录 1.简介 2.第一个Mybatis程序 搭建环境 编写代码 测试 3.CRUD(增删改查) 3.1.几个属性 3.2.select 3.3.insert 3.4.delete 3.5.upd ...

  2. ant的下载与安装——mybatis学习笔记之预备篇(一)

    看到这个标题是不是觉得有点奇怪呢--不是说mybatis学习笔记吗,怎么扯到ant了?先别急,请容我慢慢道来. mybatis是另外一个优秀的ORM框架.考虑到以后可能会用到它,遂决定提前学习,以备不 ...

  3. 【应用篇】MyBatis学习笔记

    MyBatis学习笔记 一 环境配置 1 什么是MyBatis? ​ MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架.MyBatis 消除了几乎所有的 JDBC 代码和参 ...

  4. MyBatis学习笔记(1)—使用篇

    MyBatis学习笔记(1)-使用篇 MyBatis学习笔记(2)-映射关系篇 MyBatis学习笔记(3)-高级映射之一对一映射 Mybatis学习笔记(4)-高级映射之一对多映射 Mybatis学 ...

  5. mybatis学习指南--xml文件方式篇

    mybatis学习指南---xml文件篇 以下内容是由我总结mybatis官方文档和实践中的一些经验,总共分为四篇:xml文件配置篇,java方式配置篇,缓存篇,其他配置篇,第一次这样正式的写一篇文章 ...

  6. Java源码阅读学习后的浅析和感悟(JDK篇)(持续更新)

    目录 Java源码阅读学习后的浅析和感悟(JKD篇) - 为什么阅读源码 集合框架类 - 为什么会要引入集合 - 集合结构图(部分) ArrayList集合源码分析 - 扩容机制 - 关键方法解释(D ...

  7. MyBatis多参数传递之混合方式——MyBatis学习笔记之十五

    在本系列文章的<MyBatis多参数传递之Map方式示例>一文中,网友mashiguang提问如下的方法如何传递参数:public List findStudents(Map condit ...

  8. 【Mybatis】Mybatis学习由浅入深(二)

    前言   上篇介绍到了Mybatis的优缺点,这篇接下来介绍一下流程情况和配置信息. MyBatis简介 Mybatis工作流程 加载配置信息初始化 通过配置文件或注解将配置信息加载成Statemen ...

  9. Mybatis学习日记(四)——动态SQL第一部分

    Mybatis的强大特性之一是它的动态SQL,在进行项目开发的时候,我们对数据库的操作不可能全部是定式的,当对数据库的操作根据不同情况发生变化时,就可以用到Mybatis的动态SQL.而Mybatis ...

  10. MyBatis学习系列——二级缓存

    [MyBatis学习13]MyBatis中的二级缓存 发表于2016/6/16 7:26:19  4922人阅读 分类: ● 框架技术 --[MyBatis] 1. 二级缓存的原理 前面介绍了,myb ...

最新文章

  1. 股市币市:数据分析与交易所最新公告(20190302)
  2. ASP防SQL注入攻击程序
  3. 【软件工程】UML软件
  4. saltstack批量加用户脚本
  5. %hd %d %ld %u ......
  6. 前端学习(3060):vue+element今日头条管理-处理展示文章封面
  7. 注解形式控制器配置(3)
  8. Python之数据分析(星期均值、星期汇总、Numpy的take与where方法、apply_along_axis函数)
  9. Handler通信 源码分析和手写Handler框架
  10. 野村证券分析师称微软不会收购雅虎和RIM
  11. Java-基础类库第一篇认识基础类库
  12. 俄罗斯方块、纯前端实现俄罗斯方块、俄罗斯方块代码
  13. SDL2 + OPENGL GLSL 实践
  14. 《王道计算机网络》学习笔记总目录+思维导图
  15. 在Eclipse中使用JUnit5进行单元测试
  16. BUUCTF RSA(二)
  17. 【智能优化算法-正弦余弦算法】基于反向正弦余弦算法求解高维优化问题附matlab代码
  18. duilib学习------网易云信combo控件
  19. AFPM100/B消防电源在南京基地模检具业务搬迁改造项目的应用
  20. 字节跳动面试前端岗,刷算法题有救吗?

热门文章

  1. 英特尔技术领导委员会(GTC) 专题系列之主席致辞
  2. 【手写数字识别】基于matlab GUI BP神经网络手写数字识别(手写+带面板)【含Matlab源码 1196期】
  3. 【图像配准】基于matlab互信息图像配准【含Matlab源码 1210期】
  4. oracle 同时更新多表,在Oracle数据库中同时更新两张表的简单方法
  5. mysql tx read only_DB为何大量出现select @@session.tx_read_only 详解
  6. 二进制,逆向工程,深入理解计算机系统
  7. linux重启ipv6_过渡到 nftables | Linux 中国
  8. php打印机 纸张大小,打印设置信息中的纸张类型,如何取自定义纸张!
  9. java opencv3轮廓_如何在OpenCV中获得单独的轮廓(并填充它们)?
  10. javascript 内存和连等赋值