Mybatis核心知识梳理

深入详解Mybatis的架构原理与6大核心流程 MyBatis 是 Java 生态中非常著名的一款 ORM 框架,目前在一线互联网大厂中应用广泛,Mybatis已经成为了一个必会框架。 如果你想要进入一线大厂,能够熟练使用 MyBatis 开发已经是一项非常基本的技能,同时大厂也更希望自己的开发人员深入了解 MyBatis 框架的原理和核心实现。 从这个角度看,理解 MyBatis 原理,阅读 MyBatis核心源码,这样更有利于提高职场竞争力。 在深入了解Mybatis的源码之前,我们先了解一下Mybatis的整体架构和工作原理,这样有助于我们在阅读. 阅读详情

目录

一.Mybatis架构原理

1.Mybatis入门该网站总结的非常详细快速回顾下基础

2.Mybatis架构原理

2.1架构设计 

2.2主要构件及其相互关系 

2.3总体流程

二.Mybatis缓存

(1)一级缓存原理探究与源码分析

一级缓存概念理解

一级缓存源码分析

(2)二级缓存

基础:

源码分析:

三.Mybatis延迟加载

(1)延迟加载原理

 四.Mybatis插件

插件简介

Mybatis插件介绍

Mybatis插件原理

插件接口

自定义插件

五. 通用 mapper

什么是通用Mapper

如何使用




一.Mybatis架构原理

1.Mybatis入门该网站总结的非常详细快速回顾下基础

http://c.biancheng.net/view/4309.html 

2.Mybatis架构原理

2.1架构设计 

 

我们把Mybatis的功能架构分为三层: 

(1) API接口层:提供给外部使用的接口 API,开发人员通过这些本地API来操纵数据库。接口层一接收到 调用请求就会调用数据处理层来完成具体的数据处理。 MyBatis和数据库的交互有两种方式: a. 使用传统的MyBati s提供的API ; b. 使用Mapper代理的方式

(2) 数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根 据调用的请求完成一次数据库操作。

(3) 基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是 共 用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。

2.2主要构件及其相互关系 

构建

描述

SqlSession

作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能

Executor

MyBatis执行器,是MyBatis调度的核心,负责SQL语句的生成和查询缓 存的维护

StatementHandler

封装了JDBC Statement操作,负责对JDBC statement的操作,如设置参数、将Statement结果集转换成List集合。

ParameterHandler

负责对用户传递的参数转换成JDBC Statement所需要的参数

ResultSetHandler

负责将JDBC返回的ResultSet结果集对象转换成List类型的集合

TypeHandler

负责java数据类型和jdbc数据类型之间的映射和转换

MappedStatement

MappedStatement维护了一条<select|update|insert|delete>节点的封装

SqlSouce

负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封

BoundSql

表示动态生成的SQL语句以及相应的参数信息

图解:

2.3总体流程

  1. 加载配置并初始化 触发条件:加载配置文件 配置来源于两个地方,一个是配置文件(主配置文件conf.xml,mapper文件*.xml),—个是java代码中的 注 解,将主配置文件内容解析封装到Configuration,将sql的配置信息加载成为一个mappedstatement 对 象,存储在内存之中 
  2.  接收调用请求 触发条件:调用Mybatis提供的API 传入参数:为SQL的ID和传入参数对象 处理过程:将请求传递给下层的请求处理层进行处理。
  3.  处理操作请求 触发条件:API接口层传递请求过来 传入参数:为SQL的ID和传入参数对象 处理过程: (A) 根据SQL的ID查找对应的MappedStatement对象。 (B) 根据传入参数对象解析MappedStatement对象,得到最终要执行的SQL和执行传入参数。 (C) 获取数据库连接,根据得到的最终SQL语句和执行传入参数到数据库执行,并得到执行结果。 (D) 根据MappedStatement对象中的结果映射配置对得到的执行结果进行转换处理,并得到最终的处理 结果。 (E) 释放连接资源。 
  4. 返回处理结果 将最终的处理结果返回。

二.Mybatis缓存

(1)一级缓存原理探究与源码分析

一级缓存概念理解

例如 一张用户表User包含id,name字段:

1、第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从 数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。

2、 如果中间sqlSession去执行commit操作(执行插入、更新、删除),则会清空SqlSession中的 一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

3、 第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息

一级缓存源码分析

一级缓存也绕不开SqlSession,如下图:

 

    看了一下,发现上述所有方法中,好像只有clearCache()和缓存沾点关系,那直接从这个方法入手吧,分析源码时,我们要看它(此类)是谁,它的父类和子类分别又是谁,对如上关系了解了,你才会对这个类有更深的认识,分析了一圈,你可能会得到如下这个流程图:

再深入分析,流程走到Perpetualcache中的clear()方法之后,会调用其cache.clear()方法,那 么这个cache是什么东西呢?点进去发现,cache其实就是private Map cache = new  HashMap();也就是一个Map,所以说cache.clear()其实就是map.clear(),也就是说,缓存其实就是 本地存放的一个map对象,每一个SqISession都会存放一个map对象的引用,那么这个cache是何 时创建的呢?

你觉得最有可能创建缓存的地方是哪里呢?我觉得是Executor,为什么这么认为?因为Executor是 执行器,用来执行SQL请求,而且清除缓存的方法也在Executor中执行,所以很可能缓存的创建也很 有可能在Executor中,看了一圈发现Executor中有一个createCacheKey方法,这个方法很像是创 建缓存的方法啊,跟进去看看,你发现createCacheKey方法是由BaseExecutor执行的,代码如下

CacheKey cacheKey = new CacheKey();

//MappedStatement 的 id

// id就是Sql语句的所在位置包名+类名+ SQL名称

cacheKey.update(ms.getId());

// offset 就是 0

cacheKey.update(rowBounds.getOffset());

// limit 就是 Integer.MAXVALUE

cacheKey.update(rowBounds.getLimit());

//具体的SQL语句

cacheKey.update(boundSql.getSql());

//后面是update 了 sql中带的参数

cacheKey.update(value);

...

if (configuration.getEnvironment() != null) {

// issue #176

cacheKey.update(configuration.getEnvironment().getId());

}

创建缓存key会经过一系列的update方法,udate方法由一个CacheKey这个对象来执行的,这个update方法最终由updateList的list来把五个值存进去,对照上面的代码和下面的图示,你应该能 理解这五个值都是什么了。

这里需要注意一下最后一个值,configuration.getEnvironment().getId()这是什么,这其实就是 定义在mybatis-config.xml中的标签,见如下

<environments default="development">

<environment id="development">

<transactionManager type="JDBC"/>

<dataSource type="POOLED">

<property name="driver" value="${jdbc.driver}"/>

<property name="url" value="${jdbc.url}"/>

<property name="username" value="${jdbc.username}"/>

<property name="password" value="${jdbc.password}"/>

</dataSource>

</environment>

</environments>

那么我们回归正题,那么创建完缓存之后该用在何处呢?总不会凭空创建一个缓存不使用吧?绝对不会的,经过我们对一级缓存的探究之后,我们发现一级缓存更多是用于查询操作,毕竟一级缓存也叫做查询缓存吧,为什么叫查询缓存我们一会儿说。我们先来看一下这个缓存到底用在哪了,我们跟踪到query方法如下:

Override

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds

rowBounds, ResultHandler resultHandler) throws SQLException {

BoundSql boundSql = ms.getBoundSql(parameter);

//创建缓存

CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);

return query(ms, parameter, rowBounds, resultHandler, key, boundSql);

}

@SuppressWarnings("unchecked")

Override

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds

rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws

SQLException {

...

list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;

if (list != null) {

//这个主要是处理存储过程用的。

handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);

} else {

list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key,

boundSql);

}

...

}

// queryFromDatabase 方法

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter,

RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql

boundSql) throws SQLException {

List<E> list;

localCache.putObject(key, EXECUTION_PLACEHOLDER);

try {

list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);

} finally {

localCache.removeObject(key);

}

localCache.putObject(key, list);

if (ms.getStatementType() == StatementType.CALLABLE) {

localOutputParameterCache.putObject(key, parameter);

}

return list;

}

如果查不到的话,就从数据库查,在queryFromDatabase中,会对localcache进行写入。 localcache对象的put方法最终交给Map进行存放。

private Map<Object, Object> cache = new HashMap<Object, Object>();

@Override

public void putObject(Object key, Object value) { cache.put(key, value);

}

(2)二级缓存

二级缓存的原理和一级缓存原理一样,第一次查询,会将数据放入缓存中,然后第二次查询则会直接去缓存中取。但是一级缓存是基于sqlSession的,而二级缓存是基于mapper文件的namespace的,也 就是说多个sqlSession可以共享一个mapper中的二级缓存区域,并且如果两个mapper的namespace 相同,即使是两个mapper,那么这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域中

如何使用二级缓存

一、开启二级缓存

和一级缓存默认开启不一样,二级缓存需要我们手动开启

首先在全局配置文件sqlMapConfig.xml文件中加入如下代码

<!--开启二级缓存-->

<settings>

    <setting name="cacheEnabled" value="true"/>

  </settings>

其次在例如:UserMapper.xml文件中开启缓存

<!--开启二级缓存-->

<cache></cache>

我们可以看到mapper.xml文件中就这么一个空标签,其实这里可以配置,PerpetualCache这个类是mybatis默认实现缓存功能的类。我们不写type就使用mybatis默认的缓存,也可以去实现Cache接口来自定义缓存

 

public class PerpetualCache implements Cache {

private final String id;

private MapcObject, Object> cache = new HashMapC);

public PerpetualCache(St ring id) { this.id = id;

}

我们可以看到二级缓存底层还是HashMap结构

public class User implements Serializable(

//用户ID

private int id;

//用户姓名

private String username;

//用户性别

private String sex;

}

开启了二级缓存后,还需要将要缓存的pojo实现Serializable接口,为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一定只存在内存中,有可能存在硬盘中,如果我们要再取这个缓存的话,就需要反序列化了。所以mybatis中的pojo都去实现Serializable接口。

二、useCache和flushCache

mybatis中还可以配置userCache和flushCache等配置项,userCache是用来设置是否禁用二级缓 存的,在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出 sql去查询,默认情况是true,即该sql使用二级缓存。

<select id="selectUserByUserId" useCache="false"

resultType="com.lagou.pojo.User" parameterType="int">

select * from user where id=#{id}

</select>

    这种情况是针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存,直接从数据库中获取。

    在mapper的同一个namespace中,如果有其它insert、update, delete操作数据后需要刷新缓 存,如果不执行刷新缓存会出现脏读。

设置statement配置中的flushCache="true”属性,默认情况下为true,即刷新缓存,如果改成false则 不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。

<select id="selectUserByUserId" flushCache="true" useCache="false"

resultType="com.lagou.pojo.User" parameterType="int">

select * from user where id=#{id}

</select>

一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。所以我们不用设置,默认即可。

  1. Redis整合二级缓存

基础:

上面我们介绍了 mybatis自带的二级缓存,但是这个缓存是单服务器工作,无法实现分布式缓存。 那么什么是分布式缓存呢?假设现在有两个服务器1和2,用户访问的时候访问了 1服务器,查询后的缓 存就会放在1服务器上,假设现在有个用户访问的是2服务器,那么他在2服务器上就无法获取刚刚那个 缓存,

如下图所示:

为了解决这个问题,就得找一个分布式的缓存,专门用来存储缓存数据的,这样不同的服务器要缓存数据都往它那里存,取缓存数据也从它那里取,如下图所示:

如上图所示,在几个不同的服务器之间,我们使用第三方缓存框架,将缓存都放在这个第三方框架中, 然后无论有多少台服务器,我们都能从缓存中获取数据。

         这里我们介绍mybatis与redis的整合。刚刚提到过,mybatis提供了一个eache接口,如果要实现自己的缓存逻辑,实现cache接口开发即可。mybatis本身默认实现了一个,但是这个缓存的实现无法实现分布式缓存,所以我们要自己来实现。

     redis分布式缓存就可以,mybatis提供了一个针对cache接口的redis实现类,该类存在mybatis-redis包中。

实现:

  1. pom文件

<dependency>

<groupId>org.mybatis.caches</groupId>

<artifactId>mybatis-redis</artifactId>

<version>1.0.0-beta2</version>

</dependency>

        2.配置文件

             Mapper.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>

       3.Redis.properties

redis.host=localhost

redis.port=6379

redis.connectionTimeout=5000

redis.password=

redis.database=0

      4.测试

@Test

public void SecondLevelCache(){

SqlSession sqlSession1 = sqlSessionFactory.openSession();

SqlSession sqlSession2 = sqlSessionFactory.openSession();

SqlSession sqlSession3 = sqlSessionFactory.openSession();

IUserMapper mapper1 = sqlSession1.getMapper(IUserMapper.class);

lUserMapper mapper2 = sqlSession2.getMapper(lUserMapper.class);

lUserMapper mapper3 = sqlSession3.getMapper(IUserMapper.class);

User user1 = mapper1.findUserById(1);

sqlSession1.close(); //清空一级缓存

User user = new User();

user.setId(1);

user.setUsername("lisi");

mapper3.updateUser(user);

sqlSession3.commit();

User user2 = mapper2.findUserById(1);

System.out.println(user1==user2);

}

源码分析:

RedisCache和大家普遍实现Mybatis的缓存方案大同小异,无非是实现Cache接口,并使用jedis操作缓存;不过该项目在设计细节上有一些区别;

public final class RedisCache implements Cache {

public RedisCache(final String id) {

if (id == null) {

throw new IllegalArgumentException("Cache instances require anID");

}

this.id = id;

RedisConfig redisConfig =

RedisConfigurationBuilder.getInstance().parseConfiguration();

pool = new JedisPool(redisConfig, redisConfig.getHost(),

redisConfig.getPort(),

redisConfig.getConnectionTimeout(),

redisConfig.getSoTimeout(), redisConfig.getPassword(),

redisConfig.getDatabase(), redisConfig.getClientName());

}

   RedisCache在mybatis启动的时候,由MyBatis的CacheBuilder创建,创建的方式很简单,就是调用RedisCache的带有String参数的构造方法,即RedisCache(String id);而在RedisCache的构造方法中,调用了 RedisConfigurationBuilder 来创建 RedisConfig 对象,并使用 RedisConfig 来创建JedisPool。

   RedisConfig类继承了 JedisPoolConfig,并提供了 host,port等属性的包装,简单看一下RedisConfig的属性:

public class RedisConfig extends JedisPoolConfig {

private String host = Protocol.DEFAULT_HOST;

private int port = Protocol.DEFAULT_PORT;

private int connectionTimeout = Protocol.DEFAULT_TIMEOUT;

private int soTimeout = Protocol.DEFAULT_TIMEOUT;

private String password;

private int database = Protocol.DEFAULT_DATABASE;

private String clientName;

RedisConfig对象是由RedisConfigurationBuilder创建的,简单看下这个类的主要方法:

public RedisConfig parseConfiguration(ClassLoader classLoader) {

Properties config = new Properties();

InputStream input =

classLoader.getResourceAsStream(redisPropertiesFilename);

if (input != null) {

try {

config.load(input);

} catch (IOException e) {

throw new RuntimeException(

"An error occurred while reading classpath property '"

+ redisPropertiesFilename

+ "', see nested exceptions", e);

} finally {

try {

input.close();

} catch (IOException e) {

// close quietly

}

}

}

RedisConfig jedisConfig = new RedisConfig();

setConfigProperties(config, jedisConfig);

return jedisConfig;

}

核心的方法就是parseConfiguration方法,该方法从classpath中读取一个redis.properties文件:

host=localhost

port=6379

connectionTimeout=5000

soTimeout=5000

password= database=0 clientName=

并将该配置文件中的内容设置到RedisConfig对象中,并返回;接下来,就是RedisCache使用RedisConfig类创建完成JedisPool;在RedisCache中实现了一个简单的模板方法,用来操作Redis:

private Object execute(RedisCallback callback) {

Jedis jedis = pool.getResource();

try {

return callback.doWithRedis(jedis);

} finally {

jedis.close();

}

}

模板接口为RedisCallback,这个接口中就只需要实现了一个doWithRedis方法而已:

public interface RedisCallback {

Object doWithRedis(Jedis jedis);

}

接下来看看Cache中最重要的两个方法:putObject和getObject,通过这两个方法来查看mybatis-redis储存数据的格式:

@Override

public void putObject(final Object key, final Object value) {

execute(new RedisCallback() {

@Override

public Object doWithRedis(Jedis jedis) {

jedis.hset(id.toString().getBytes(), key.toString().getBytes(),

SerializeUtil.serialize(value));

return null;

}

});

}

@Override

public Object getObject(final Object key) {

return execute(new RedisCallback() {

@Override

public Object doWithRedis(Jedis jedis) {

return SerializeUtil.unserialize(jedis.hget(id.toString().getBytes(),

key.toString().getBytes()));

}

});

}

可以很清楚的看到,mybatis-redis在存储数据的时候,是使用的hash结构,把cache的id作为这个hash的key (cache的id在mybatis中就是mapper的namespace);这个mapper中的查询缓存数据作为 hash的field,需要缓存的内容直接使用SerializeUtil存储,SerializeUtil和其他的序列化类差不多,负责对象的序列化和反序列化;

三.Mybatis延迟加载

(1)延迟加载原理

延迟加载:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载。

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

2.它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。当然了,不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的。

 四.Mybatis插件

  1. 插件原理

插件简介

   一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展。这样的好处是显而易见的,一是增加了框架的灵活性。二是开发者可以结合实际需求,对框架进行拓展,使其能够更好的工作。以MyBatis为例,我们可基于MyBati s插件机制实现分页、分表,监控等功能。由于插件和业务无关,业务也无法感知插件的存在。因此可以无感植入插件,在无形中增强功能。

Mybatis插件介绍

   Mybatis作为一个应用广泛的优秀的ORM开源框架,这个框架具有强大的灵活性,在四大组件(Executor、StatementHandler、ParameterHandler、ResultSetHandler)处提供了简单易用的插 件扩展机制。Mybatis对持久层的操作就是借助于四大核心对象。MyBatis支持用插件对四大核心对象进 行拦截,对mybatis来说插件就是拦截器,用来增强核心对象的功能,

增强功能本质上是借助于底层的 动态代理实现的,换句话说,MyBatis中的四大对象都是代理对象。

MyBatis所允许拦截的方法如下:

执行器Executor (update、query、commit、rollback等方法);

SQL语法构建器StatementHandler (prepare、parameterize、batch、updates query等方 法);

参数处理器ParameterHandler (getParameterObject、setParameters方法);

结果集处理器ResultSetHandler (handleResultSets、handleOutputParameters等方法);

Mybatis插件原理

在四大对象创建的时候

1、每个创建出来的对象不是直接返回的,而是interceptorChain.pluginAll(parameterHandler);

2、获取到所有的Interceptor (拦截器)(插件需要实现的接口);调用 interceptor.plugin(target);返回 target 包装后的对象

3、插件机制,我们可以使用插件为目标对象创建一个代理对象;AOP (面向切面)我们的插件可 以为四大对象创建出代理对象,代理对象就可以拦截到四大对象的每一个执行

拦截

插件具体是如何拦截并附加额外的功能的呢?以ParameterHandler来说

public ParameterHandler newParameterHandler(MappedStatement mappedStatement,

Object object, BoundSql sql, InterceptorChain interceptorChain){

  ParameterHandler parameterHandler =

mappedStatement.getLang().createParameterHandler(mappedStatement,object,sql);

parameterHandler = (ParameterHandler)

interceptorChain.pluginAll(parameterHandler);

return parameterHandler;

}

public Object pluginAll(Object target) {

for (Interceptor interceptor : interceptors) {

target = interceptor.plugin(target);

}

return target;

}

interceptorChain保存了所有的拦截器(interceptors),是mybatis初始化的时候创建的。调用拦截器链中的拦截器依次的对目标进行拦截或增强。interceptor.plugin(target)中的target就可以理解为mybatis中的四大对象。返回的target是被重重代理后的对象

如果我们想要拦截Executor的query方法,那么可以这样定义插件:

@Intercepts({

@Signature(

type = Executor.class,

method = "query",

args=

{MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class}

)

})

public class ExeunplePlugin implements Interceptor {

//省略逻辑

}

除此之外,我们还需将插件配置到sqlMapConfig.xm l中。

<plugins>

<plugin interceptor="com.lagou.plugin.ExamplePlugin">

</plugin>

</plugins>

这样MyBatis在启动时可以加载插件,并保存插件实例到相关对象(InterceptorChain,拦截器链) 中。待准备工作做完后,MyBatis处于就绪状态。我们在执行SQL时,需要先通过DefaultSqlSessionFactory 创建 SqlSession。Executor 实例会在创建 SqlSession 的过程中被创建, Executor实例创建完毕后,MyBatis会通过JDK动态代理为实例生成代理类。这样,插件逻辑即可在 Executor相关方法被调用前执行。以上就是MyBatis插件机制的基本原理

  1. 编写一个Myabtis插件

插件接口

Mybatis 插件接口-Interceptor

• Intercept方法,插件的核心方法

• plugin方法,生成target的代理对象

• setProperties方法,传递插件所需参数

自定义插件

设计实现一个自定义插件

Intercepts ({//注意看这个大花括号,也就这说这里可以定义多个@Signature对多个地方拦截,都用这

个拦截器

@Signature (type = StatementHandler .class , //这是指拦截哪个接口

    method = "prepare",//这个接口内的哪个方法名,不要拼错了

    args = { Connection.class, Integer .class}),//// 这是拦截的方法的入参,按顺

序写到这,不要多也不要少,如果方法重载,可是要通过方法名和入参来确定唯一的

})

public class MyPlugin implements Interceptor {

private final Logger logger = LoggerFactory.getLogger(this.getClass());

// //这里是每次执行操作的时候,都会进行这个拦截器的方法内

Override

public Object intercept(Invocation invocation) throws Throwable {

//增强逻辑

System.out.println("对方法进行了增强....");

return invocation.proceed(); //执行原方法

}

/**

* //主要是为了把这个拦截器生成一个代理放到拦截器链中

* ^Description包装目标对象 为目标对象创建代理对象

* @Param target为要拦截的对象

* @Return代理对象

*/

Override

public Object plugin(Object target) {

System.out.println("将要包装的目标对象:"+target);

return Plugin.wrap(target,this);

 }

 

/**获取配置文件的属性**/

//插件初始化的时候调用,也只调用一次,插件配置的属性从这里设置进来

Override

  public void setProperties(Properties properties) {

System.out.println("插件配置的初始化参数:"+properties );

}

}

sqlMapConfig.xml

<plugins>

<plugin interceptor="com.lagou.plugin.MySqlPagingPlugin">

<!--配置参数-->

<property name="name" value="Bob"/>

</plugin>

</plugins

mapper接口

public interface UserMapper {

List<User> selectUser();

}

Mapper.xml

<mapper namespace="com.lagou.mapper.UserMapper">

<select id="selectUser" resultType="com.lagou.pojo.User">

SELECT

id,username

FROM

user

</select>

</mapper>

测试类

public class PluginTest {

@Test

public void test() throws IOException {

InputStream resourceAsStream =

Resources.getResourceAsStream("sqlMapConfig.xml");

SqlSessionFactory sqlSessionFactory = new

SqlSessionFactoryBuilder().build(resourceAsStream);

SqlSession sqlSession = sqlSessionFactory.openSession();

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

List<User> byPaging = userMapper.selectUser();

for (User user : byPaging) {

System.out.println(user);

}

}

}

 

  1. pageHelper分页插件

MyBati s可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封

装,使用简单的方式即可获得分页的相关数据

开发步骤:

① 导入通用PageHelper的坐标

② 在mybatis核心配置文件中配置PageHelper插件

③ 测试分页数据获取

 

①导入通用PageHelper坐标

<dependency>

    <groupId>com.github.pagehelper</groupId>

    <artifactId>pagehelper</artifactId>

    <version>3.7.5</version>

  </dependency>

  <dependency>

    <groupId>com.github.jsqlparser</groupId>

    <artifactId>jsqlparser</artifactId>

    <version>0.9.1</version>

  </dependency>

② 在mybatis核心配置文件中配置PageHelper插件

<!--注意:分页助手的插件 配置在通用馆mapper之前*-->*

<plugin interceptor="com.github.pagehelper.PageHelper">

<!—指定方言 —>

<property name="dialect" value="mysql"/>

</plugin>

③ 测试分页代码实现

 @Test

  public void testPageHelper() {

    //设置分页参数

    PageHelper.startPage(1, 2);

    List<User> select = userMapper2.select(null);

    for (User user : select) {

      System.out.println(user);

   }

 }

}

获得分页相关的其他参数

//其他分页的数据

PageInfo<User> pageInfo = new PageInfo<User>(select);

System.out.println("总条数:"+pageInfo.getTotal());

System.out.println("总页数:"+pageInfo. getPages ());

System.out.println("当前页:"+pageInfo. getPageNum());

System.out.println("每页显万长度:"+pageInfo.getPageSize());

System.out.println("是否第一页:"+pageInfo.isIsFirstPage());

System.out.println("是否最后一页:"+pageInfo.isIsLastPage());

五. 通用 mapper

什么是通用Mapper

通用Mapper就是为了解决单表增删改查,基于Mybatis的插件机制。开发人员不需要编写SQL,不需要在DAO中增加方法,只要写好实体类,就能支持相应的增删改查方法

如何使用

  1. 首先在maven项目,在pom.xml中引入mapper的依赖

<dependency>

<groupId>tk.mybatis</groupId>

<artifactId>mapper</artifactId>

<version>3.1.2</version>

</dependency>

  1. Mybatis配置文件中完成配置

<plugins>

<!--分页插件:如果有分页插件,要排在通用mapper之前-->

<plugin interceptor="com.github.pagehelper.PageHelper">

 <property name="dialect" value="mysql"/>

</plugin>

<plugin interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor">

<!-- 通用Mapper接口,多个通用接口用逗号隔开 -->

<property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>

</plugin>

</plugins>

  1. 实体类设置主键

@Table(name = "t_user")

public class User {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Integer id;

private String username;

}

  1. 定义通用mapper

import com.lagou.domain.User;

import tk.mybatis.mapper.common.Mapper;

public interface UserMapper extends Mapper<User> {

}

  1. 测试

public class UserTest {

  @Test

  public void test1() throws IOException {

    Inputstream resourceAsStream =

Resources.getResourceAsStream("sqlMapConfig.xml");

    SqlSessionFactory build = new

SqlSessionFactoryBuilder().build(resourceAsStream);

    SqlSession sqlSession = build.openSession();

    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

    User user = new User();

    user.setId(4);

    //(1)mapper基础接口

    //select 接口

    User user1 = userMapper.selectOne(user); //根据实体中的属性进行查询,只能有 —个返回值

    List<User> users = userMapper.select(null); //查询全部结果

    userMapper.selectByPrimaryKey(1); //根据主键字段进行查询,方法参数必须包含完 整的主键属性,查询条件使用等号

    userMapper.selectCount(user); //根据实体中的属性查询总数,查询条件使用等号

    // insert 接口

    int insert = userMapper.insert(user); //保存一个实体,null值也会保存,不会使用数据库默认值

    int i = userMapper.insertSelective(user); //保存实体,null的属性不会保存, 会使用数据库默认值

    // update 接口

    int i1 = userMapper.updateByPrimaryKey(user);//根据主键更新实体全部字段,null值会被更新

    // delete 接口

    int delete = userMapper.delete(user); //根据实体属性作为条件进行删除,查询条件使用等号

    userMapper.deleteByPrimaryKey(1); //根据主键字段进行删除,方法参数必须包含完 整的主键属性

    //(2)example方法

    Example example = new Example(User.class);

    example.createCriteria().andEqualTo("id", 1);

    example.createCriteria().andLike("val", "1");

    //自定义查询

    List<User> users1 = userMapper.selectByExample(example);

 }

}

 

 

SEED-VII 实战指南:如何利用多模态脑电图数据提升情感识别精度 本文提供了SEED-VII多模态脑电图数据集的实战指南,详细介绍了如何利用该数据集提升情感识别精度。内容涵盖数据获取、预处理、特征提取,以及构建多模态融合神经网络模型的关键步骤,并强调了被试者独立评估的重要性,为情感计算研究者提供了从数据到模型的全流程解决方案。 阅读详情

相关推荐

Outlook日历同步全攻略:Win10+安卓+iOS三端无缝衔接(附常见问题解决)

本文提供了一份详尽的Outlook日历同步指南,帮助用户在Windows 10、安卓和iOS三端实现无缝衔接。文章深入解析了Exchange ActiveSync等核心同步原理,并给出了从账户创建到各设备(特别是华为手机)的具体配置步骤、进阶管理技巧以及针对同步失败、事件重复等常见问题的实战解决方案,旨在彻底解决跨平台日程管理的痛点。

l3m4n的博客 554

MyBatis架构原理

一、架构整体设计 功能架构讲解: 我们把Mybatis的功能架构分为三层: (1)API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。 (2)数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。 (3)...

sinat_34814635的博客 3093

告别ROS1!在Ubuntu 22.04上为VINS-Fusion适配ROS2 Humble的完整踩坑记录

本文详细记录了在Ubuntu 22.04上将VINS-Fusion从ROS1迁移到ROS2 Humble的完整过程,包括环境搭建、依赖管理、核心代码适配及性能优化。通过实战案例和调试技巧,帮助开发者高效解决迁移过程中的常见问题,提升系统稳定性和性能。

weixin_42628688的博客 281

Mybatis架构原理

Mybatis架构原理 我们把Mybatis的功能架构分为三层: API接口层:提供给外部使用的接口 API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。 MyBatis和数据库的交互有两种方式: a. 使用传统的MyBatis提供的API ; b. 使用Mapper代理的方式 数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。 基础支撑层:负责最基础的功能支撑,包括连接

qq_40977467的博客 334

无人机航拍图像匹配——SIFT算法实践(含代码)

无人机航拍图像匹配——SIFT算法实践(含代码)

zhangzhao147的博客 6395

Ardupilot姿态控制详解(完结篇)

本文主要是对过去写的一些APM中姿态控制流程及函数博文的汇总。 按照之前写的博文以及个人认为的学习顺序,给出APM姿态控制器的学习顺序如下: APM姿态旋转理论基础 Ardupilot姿态控制器 PID控制流程(整定后的P控制器) 详解APM的开方控制器sqrt_controller Ardupilot前馈及平滑函数input_euler_angle_roll_pitch_yaw解析 Ardupilot倾转分离函数thrust_heading_rotation_angles Ardupilot四元数姿态控制

moumde的博客 7024

MyBatis核心知识点介绍

MyBatis是一个可以自定义SQL、存储过程和高级映射的持久层框架。接口映射就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置。虽然Mybatis解析Xml映射文件是按照顺序解析的,但是,被引用的B标签依然可以定义在任何地方,Mybatis都可以正确识别。...

浅羽技术 589

Cgroup对进程所占用的CPU与内存资源同时进行限制的测试

测试环境:Ubuntu。处理器数量:1CPU。内存大小:2048MB。 以下是测试程序mem_cpu_test.c的代码。 将mem_cpu_test.c编译为可以运行的程序mem_cpu_test。 运行程序,并用top命令查看mem_cpu_test所占用的系统资源情况,可以看到,mem_cpu_test使用了50%左右的内存资源,也就是1000MB左右,并占用了接近全部的CPU资源。 关闭以上的程序后,在Cgroup子系统memory和cpu下分别创建进程组test1和test2。 并设置tes

qq_44998067的博客 1154

Mybatis简介及其核心概念

Mybatis简介 Mybatis是什么? MyBatis(3.x以前的版本叫ibatis)是一款一流的支持自定义SQL、存储过程和高级映射的持久化框架。MyBatis几 乎消除了所有的JDBC 代码,也基本不需要手工去设置参数和获取检索结果。 MyBatis 能够使用简单的XML格式或 者注解进行来配置,能够映射基本数据元素、 Map 接口和POJOs(普通java...

Logicr的博客 1931

硬件-汽车电子-TBOX/TCAM核心器件选型与系统集成设计指南

本文深入解析汽车电子TBOX/TCAM硬件架构与系统集成设计,涵盖核心器件选型、电源管理、信号完整性优化等关键点。重点分享5G模组选型避坑经验、MCU隐性指标及热设计验证方案,为车载通信系统开发提供实用指南。

weixin_30352645的博客 531

【51单片机仿真】基于51单片机设计的多功能电子时钟日历(实时时钟闹钟秒表温度检测)系统__资料编号5

该项目是基于单片机的多功能电子时钟系统,集成了时间显示、闹钟设置、秒表功能以及温度显示等功能。主要硬件包括1602液晶显示屏、DS1302时钟芯片、DS18B20温度传感器和蜂鸣器。系统通过按键进行时间和闹钟的设置,并实时显示当前时间和温度。

嵌入式阿齐Archie 2万+

MyBatis核心知识点梳理

MyBatis的知识点可归纳为“1个核心思想(ORM)+2种映射方式(XML+注解)+3个核心对象(SqlSessionFactory、SqlSession、Mapper代理)+4大核心模块(基础、配置、映射、高级特性)”。入门重点:掌握环境搭建、核心配置、基础映射(简单CRUD);进阶重点:掌握动态SQL、多表关联、懒加载、缓存、分页插件;面试重点:核心对象生命周期、动态SQL、缓存机制、ORM思想、SQL注入防范。

2301_80427638的博客 599

元数据治理实战:构建企业数据地图与血缘体系

元数据(Metadata)是描述数据的数据,是你能了解“数据是怎么来的”、“它是做什么的”、“能不能用”的基础信息。📌 举个例子:order_idstringods_order定义:平台内用户在订单系统中产生的唯一订单编号更新时间:每天凌晨 2 点,由 Airflow 调度任务生成所属人:张三(数据开发部)🔍 这些信息本身不直接参与计算,但它们是数据可管理、可追踪、可评估的基础。模块实战建议元数据类型技术 + 业务 + 操作元数据三类统一治理治理路径。

晴天彩虹雨的博客 290

MyBatis核心知识梳理

Ebbie Liu 208

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

MyBatis核心知识梳理:从配置到动态SQL(你想要的都在这)

本文系统梳理了MyBatis框架的核心知识点,涵盖Maven项目构建、配置文件、动态Mapper、resultMap映射、动态SQL、分页与缓存机制,对各项配置的用途和常见注意事项进行了说明,适合作为学习MyBatis的参考。

2603_95954545的博客 211

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

【JAVA 进阶】SpringMVC全面解析:从入门到实战的核心知识点梳理

本文系统介绍了SpringMVC框架的核心概念与应用。首先概述了MVC设计模式和SpringMVC的优势,包括与Spring生态的无缝集成、灵活配置等特点。重点解析了SpringMVC的核心组件(DispatcherServlet、HandlerMapping等)及其协同工作流程,详细阐述了请求从接收到响应的完整执行过程。

CodeSuc 的技术博客 2963

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

MyBatis 核心知识体系整理 【面试专题】

MyBatis是一个半自动ORM框架,通过XML/注解配置实现Java对象与数据库的映射,相比全自动框架更灵活。

小二丶的博客 1219

MyBatis 核心类详解与架构解析:从入门到源码级理解

类名职责简述构建创建SqlSession提供数据库操作 APIExecutorSQL 执行引擎单条 SQL 的封装全局配置管理器JDBC Statement 操作设置 SQL 参数Java 与 JDBC 类型转换结果集处理。

呜呼哈 1103

SSM 框架整合核心知识点总结

SSM 整合的核心是Spring 作为胶水框架表现层:SpringMVC 负责请求接收与响应,通过 Spring 容器注入 Service业务层:Spring 管理 Service,通过声明式事务保证数据一致性,注入 DAO持久层:MyBatis 负责数据访问,由 Spring 接管数据源、会话工厂和 Mapper 代理整合后实现了分层解耦、组件复用、事务统一管理,是 Java Web 开发中高效、稳定的技术方案,也是后续学习 Spring Boot 的基础。

hyh1795844373的博客 1225

MyBatis面试宝典:从基础到高级全面解析(2025持续更新版)

参考答案这是MyBatis中最核心的基础面试题,涉及安全与原理。特性#{}(占位符替换)${}(字符串拼接)处理方式预编译处理。MyBatis会将其解析为JDBC的?,然后使用的set方法安全地设置参数。字符串直接替换。MyBatis会将其替换为变量的实际值,拼接在SQL语句中。安全性高,能有效防止SQL注入。低,如果参数值来自用户输入,极易引发SQL注入攻击。性能高。数据库可以对预编译的SQL进行缓存,优化执行计划。低。每次都需要对完整的SQL语句进行编译。适用场景几乎所有参数传递场景。

用文字记录技术,用分享回馈社区,用坚持见证成长 822

mybatis完全笔记_知识点和易错点总结

### 知识点总结 ###   1. 增删改查自主实现 查 普通代码模板如下 @Test public void test1() throws IOException { InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory f = new SqlSessionFa...

qq_41525101的博客 503

Java架构师面试题及核心知识资源集

Java架构师面试题及核心知识资源集 去发现同类优质开源项目:https://gitcode.com/ 资源介绍 本资源仓库包含两份珍贵的资料,旨在帮助广大Java工程师和架构师在面试中脱颖而出,以及在日常工作中巩固和提升技术栈。 资源一:1000道互联网Java架构师面试题.pdf 这是一本涵盖Java架构师面试常见题目的宝典,内容包括但不限于以下技术栈: Java 基础 MyBatis Zo...

gitblog_06798的博客 503

java九大阶段知识体系梳理

java技术体系梳理

gb4215287的博客 397

Mybatis学习总结—思维导图

本文整理了Mybatis的知识点有十三大分支 一、Mybatis 入门 二、Mybatis 的基本使用 三、Java 日志处理框架 四、Mybatis 配置完善 五、SqlSession 常用 API 六、Mapper 动态代理 七、动态 SQL 八、Mybatis 缓存 九、Mybatis 多表关联查询 十、Mybatis 注解的使用 十一、Mybatis Generator 工具的使用 十二、PageHelper 分页插件 十三、Mybatis 与 Servlet 整合 添加链接描述 ...

Kenji34的博客 531

springboot mybatis

MyBatis 常用关键字 = SQL 关键字 + 动态 SQL 标签 + #{} / ${} + resultMap + 缓存配置。

a378113472的博客 405

考试知识点梳理

核心定位:Spring Boot 是 Spring 框架的扩展,旨在简化 Spring 应用的初始搭建和开发过程。与传统 Spring 框架的区别传统 Spring 需要大量 XML 或 Java 配置,Spring Boot 通过自动配置减少配置量。Spring Boot 提供起步依赖(Starter),一个依赖包含多个相关库,简化 Maven 管理。Spring Boot 内置 Web 服务器(默认 Tomcat),可打包成可执行 JAR 直接运行,无需外部部署。定位。

wyhwust的博客 866
上一篇: 并发编程之AQS
下一篇: Mybatis中应用到的设计模式
Crazy Code Farmer
博客等级 码龄10年 315粉丝 87原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值