MyBatis——MyBatis插件原理

这篇具有很好参考价值的文章主要介绍了MyBatis——MyBatis插件原理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

摘要

本博文主要介绍MyBatis插件机原理,帮助大家更好的理解和学习MyBatis。

一、插件机制概述

MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) 拦截执行器的方法
  • ParameterHandler (getParameterObject, setParameters) 拦截参数的处理
  • ResultSetHandler (handleResultSets, handleOutputParameters) 拦截结果集的处理
  • StatementHandler (prepare, parameterize, batch, update, query) 拦截Sql语法构建的处理

Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的),由于插件会深入到Mybatis的核心,因此在编写自己的插件前最好了解下它的原理,以便写出安全高效的插件。

没有插件的运行过程

MyBatis——MyBatis插件原理,Mybatis,mybatis,java,开发语言

有插件的运行过程

MyBatis——MyBatis插件原理,Mybatis,mybatis,java,开发语言

一旦配置上插件,ParameterHandler,ResultSetHandler,StatementHandler,Executor这四大核心对象,将会生成 “变身”,是一种代理对象,而不再是 “原身”。

如下是时序图,在整个时序图中,涉及到mybatis插件部分已标红,基本上就是体现在上文中提到的四个类上,对这些类上的方法进行拦截。

MyBatis——MyBatis插件原理,Mybatis,mybatis,java,开发语言

二、拦截器的使用

首先我们看下MyBatis拦截器的接口定义:

public interface Interceptor {

  Object intercept(Invocation invocation) throws Throwable;

  Object plugin(Object target);

  void setProperties(Properties properties);

}

比较简单,只有3个方法。 MyBatis默认没有一个拦截器接口的实现类,开发者们可以实现符合自己需求的拦截器。下面的MyBatis官网的一个拦截器实例:

@Intercepts({@Signature(type= Executor.class, method = "update", args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  public Object intercept(Invocation invocation) throws Throwable {
    return invocation.proceed();
  }
  public Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
  public void setProperties(Properties properties) {
  }
}

全局xml配置:

<plugins>
    <plugin interceptor="org.format.mybatis.cache.interceptor.ExamplePlugin"></plugin>
</plugins>

这个拦截器拦截Executor接口的update方法(其实也就是SqlSession的新增,删除,修改操作),所有执行executor的update方法都会被该拦截器拦截到。

三、源码分析

首先从源头->配置文件开始分析:XMLConfigBuilder解析MyBatis全局配置文件的pluginElement私有方法:

private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
        for (XNode child : parent.getChildren()) {
            String interceptor = child.getStringAttribute("interceptor");
            Properties properties = child.getChildrenAsProperties();
            Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
            interceptorInstance.setProperties(properties);
            configuration.addInterceptor(interceptorInstance);
        }
    }
}

具体的解析代码其实比较简单,就不贴了,主要就是通过反射实例化plugin节点中的interceptor属性表示的类。然后调用全局配置类Configuration的addInterceptor方法。

public void addInterceptor(Interceptor interceptor) {
    interceptorChain.addInterceptor(interceptor);
}

这个interceptorChain是Configuration的内部属性,类型为InterceptorChain,也就是一个拦截器链,我们来看下它的定义:

public class InterceptorChain {

    private final List<Interceptor> interceptors = new ArrayList<Interceptor>();

    public Object pluginAll(Object target) {
        for (Interceptor interceptor : interceptors) {
            target = interceptor.plugin(target);
        }
        return target;
    }

    public void addInterceptor(Interceptor interceptor) {
        interceptors.add(interceptor);
    }

    public List<Interceptor> getInterceptors() {
        return Collections.unmodifiableList(interceptors);
    }

}

现在我们理解了拦截器配置的解析以及拦截器的归属,现在我们回过头看下为何拦截器会拦截这些方法(Executor,ParameterHandler,ResultSetHandler,StatementHandler的部分方法):

public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
}

public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
}

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
}

public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
        executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
        executor = new ReuseExecutor(this, transaction);
    } else {
        executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
        executor = new CachingExecutor(executor, autoCommit);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
}

以上4个方法都是Configuration的方法。这些方法在MyBatis的一个操作(新增,删除,修改,查询)中都会被执行到,执行的先后顺序是Executor,ParameterHandler,ResultSetHandler,StatementHandler(其中ParameterHandler和ResultSetHandler的创建是在创建StatementHandler[3个可用的实现类CallableStatementHandler,PreparedStatementHandler,SimpleStatementHandler]的时候,其构造函数调用的[这3个实现类的构造函数其实都调用了父类BaseStatementHandler的构造函数])。

这4个方法实例化了对应的对象之后,都会调用interceptorChain的pluginAll方法,InterceptorChain的pluginAll刚才已经介绍过了,就是遍历所有的拦截器,然后调用各个拦截器的plugin方法。注意:拦截器的plugin方法的返回值会直接被赋值给原先的对象。

由于可以拦截StatementHandler,这个接口主要处理sql语法的构建,因此比如分页的功能,可以用拦截器实现,只需要在拦截器的plugin方法中处理StatementHandler接口实现类中的sql即可,可使用反射实现。

MyBatis还提供了@Intercepts和 @Signature关于拦截器的注解。官网的例子就是使用了这2个注解,还包括了Plugin类的使用:

@Override
public Object plugin(Object target) {
    return Plugin.wrap(target, this);
}

四、代理链的生成

Mybatis支持对Executor、StatementHandler、ParameterHandler和ResultSetHandler进行拦截,也就是说会对这4种对象进行代理。通过查看Configuration类的源代码我们可以看到,每次都对目标对象进行代理链的生成。

下面以Executor为例。Mybatis在创建Executor对象时会执行下面一行代码:

executor =(Executor) interceptorChain.pluginAll(executor);

InterceptorChain里保存了所有的拦截器,它在mybatis初始化的时候创建。上面这句代码的含义是调用拦截器链里的每个拦截器依次对executor进行plugin(插入?)代码如下:

/** 
  * 每一个拦截器对目标类都进行一次代理 
  * @param target 
  * @return 层层代理后的对象 
  */  
 public Object pluginAll(Object target) {  
     for(Interceptor interceptor : interceptors) {  
         target= interceptor.plugin(target);  
     }  
     return target;  
 } 

下面以一个简单的例子来看看这个plugin方法里到底发生了什么:

@Intercepts({@Signature(type = Executor.class, method ="update", args = {MappedStatement.class, Object.class})})  
public class ExamplePlugin implements Interceptor {  
    @Override  
    public Object intercept(Invocation invocation) throws Throwable {  
        return invocation.proceed();  
    }  
  
    @Override  
    public Object plugin(Object target) {  
        return Plugin.wrap(target, this);  
    }  
  
    @Override  
    public void setProperties(Properties properties) {  
    }
} 

每一个拦截器都必须实现上面的三个方法,其中:

  • Object intercept(Invocation invocation)是实现拦截逻辑的地方,内部要通过invocation.proceed()显式地推进责任链前进,也就是调用下一个拦截器拦截目标方法。

  • Object plugin(Object target) 就是用当前这个拦截器生成对目标target的代理,实际是通过Plugin.wrap(target,this)来完成的,把目标target和拦截器this传给了包装函数。

  • setProperties(Properties properties)用于设置额外的参数,参数配置在拦截器的Properties节点里。

注解里描述的是指定拦截方法的签名 [type,method,args] (即对哪种对象的哪种方法进行拦截),它在拦截前用于决断。

定义自己的Interceptor最重要的是要实现plugin方法和intercept方法,在plugin方法中我们可以决定是否要进行拦截进而决定要返回一个什么样的目标对象。而intercept方法就是要进行拦截的时候要执行的方法。

对于plugin方法而言,其实Mybatis已经为我们提供了一个实现。Mybatis中有一个叫做Plugin的类,里面有一个静态方法wrap(Object target,Interceptor interceptor),通过该方法可以决定要返回的对象是目标对象还是对应的代理。这里我们先来看一下Plugin的源码:

package org.apache.ibatis.plugin;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.ibatis.reflection.ExceptionUtil;

//这个类是Mybatis拦截器的核心,大家可以看到该类继承了InvocationHandler
//又是JDK动态代理机制
public class Plugin implements InvocationHandler {

  //目标对象
  private Object target;
  //拦截器
  private Interceptor interceptor;
  //记录需要被拦截的类与方法
  private Map<Class<?>, Set<Method>> signatureMap;

  private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
    this.target = target;
    this.interceptor = interceptor;
    this.signatureMap = signatureMap;
  }

  //一个静态方法,对一个目标对象进行包装,生成代理类。
  public static Object wrap(Object target, Interceptor interceptor) {
    //首先根据interceptor上面定义的注解 获取需要拦截的信息
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    //目标对象的Class
    Class<?> type = target.getClass();
    //返回需要拦截的接口信息
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    //如果长度为>0 则返回代理类 否则不做处理
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }

  //代理对象每次调用的方法
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      //通过method参数定义的类 去signatureMap当中查询需要拦截的方法集合
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      //判断是否需要拦截
      if (methods != null && methods.contains(method)) {
        return interceptor.intercept(new Invocation(target, method, args));
      }
      //不拦截 直接通过目标对象调用方法
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }

  //根据拦截器接口(Interceptor)实现类上面的注解获取相关信息
  private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
    //获取注解信息
    Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
    //为空则抛出异常
    if (interceptsAnnotation == null) { // issue #251
      throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());      
    }
    //获得Signature注解信息
    Signature[] sigs = interceptsAnnotation.value();
    Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();
    //循环注解信息
    for (Signature sig : sigs) {
      //根据Signature注解定义的type信息去signatureMap当中查询需要拦截方法的集合
      Set<Method> methods = signatureMap.get(sig.type());
      //第一次肯定为null 就创建一个并放入signatureMap
      if (methods == null) {
        methods = new HashSet<Method>();
        signatureMap.put(sig.type(), methods);
      }
      try {
        //找到sig.type当中定义的方法 并加入到集合
        Method method = sig.type().getMethod(sig.method(), sig.args());
        methods.add(method);
      } catch (NoSuchMethodException e) {
        throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
      }
    }
    return signatureMap;
  }

  //根据对象类型与signatureMap获取接口信息
  private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
    Set<Class<?>> interfaces = new HashSet<Class<?>>();
    //循环type类型的接口信息 如果该类型存在与signatureMap当中则加入到set当中去
    while (type != null) {
      for (Class<?> c : type.getInterfaces()) {
        if (signatureMap.containsKey(c)) {
          interfaces.add(c);
        }
      }
      type = type.getSuperclass();
    }
    //转换为数组返回
    return interfaces.toArray(new Class<?>[interfaces.size()]);
  }

}

下面是俩个注解类的定义源码:

package org.apache.ibatis.plugin;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {
  Signature[] value();
}
package org.apache.ibatis.plugin;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Signature {
  Class<?> type();

  String method();

  Class<?>[] args();
}

五、Plugin.wrap方法

从前面可以看出,每个拦截器的plugin方法是通过调用Plugin.wrap方法来实现的。代码如下:

public static Object wrap(Object target, Interceptor interceptor) {  
   // 从拦截器的注解中获取拦截的类名和方法信息  
   Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);  
   Class<?> type = target.getClass();  
   // 解析被拦截对象的所有接口(注意是接口)  
   Class<?>[] interfaces = getAllInterfaces(type, signatureMap);  
   if(interfaces.length > 0) {  
        // 生成代理对象, Plugin对象为该代理对象的InvocationHandler  (InvocationHandler属于java代理的一个重要概念,不熟悉的请参考相关概念)  
        return Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target,interceptor,signatureMap));  
    }  
    return target;  
}

这个Plugin类有三个属性:

private Object target;// 被代理的目标类

private Interceptor interceptor;// 对应的拦截器

private Map<Class<?>, Set<Method>> signatureMap;// 拦截器拦截的方法缓存

getSignatureMap方法

private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
    Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
    if (interceptsAnnotation == null) { // issue #251
      throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());      
    }
    Signature[] sigs = interceptsAnnotation.value();
    Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();
    for (Signature sig : sigs) {
      Set<Method> methods = signatureMap.get(sig.type());
      if (methods == null) {
        methods = new HashSet<Method>();
        signatureMap.put(sig.type(), methods);
      }
      try {
        Method method = sig.type().getMethod(sig.method(), sig.args());
        methods.add(method);
      } catch (NoSuchMethodException e) {
        throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
      }
    }
    return signatureMap;
}

getSignatureMap方法解释:首先会拿到拦截器这个类的 @Interceptors注解,然后拿到这个注解的属性 @Signature注解集合,然后遍历这个集合,遍历的时候拿出 @Signature注解的type属性(Class类型),然后根据这个type得到带有method属性和args属性的Method。由于 @Interceptors注解的 @Signature属性是一个属性,所以最终会返回一个以type为key,value为Set<Method>的Map。

@Intercepts({@Signature(type= Executor.class, method = "update", args = {MappedStatement.class,Object.class})})

比如这个 @Interceptors注解会返回一个key为Executor,value为集合(这个集合只有一个元素,也就是Method实例,这个Method实例就是Executor接口的update方法,且这个方法带有MappedStatement和Object类型的参数)。这个Method实例是根据 @Signature的method和args属性得到的。如果args参数跟type类型的method方法对应不上,那么将会抛出异常。

getAllInterfaces方法

private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
    Set<Class<?>> interfaces = new HashSet<Class<?>>();
    while (type != null) {
      for (Class<?> c : type.getInterfaces()) {
        if (signatureMap.containsKey(c)) {
          interfaces.add(c);
        }
      }
      type = type.getSuperclass();
    }
    return interfaces.toArray(new Class<?>[interfaces.size()]);
}

getAllInterfaces方法解释: 根据目标实例target(这个target就是之前所说的MyBatis拦截器可以拦截的类,Executor,ParameterHandler,ResultSetHandler,StatementHandler)和它的父类们,返回signatureMap中含有target实现的接口数组。

所以Plugin这个类的作用就是根据 @Interceptors注解,得到这个注解的属性 @Signature数组,然后根据每个 @Signature注解的type,method,args属性使用反射找到对应的Method。最终根据调用的target对象实现的接口决定是否返回一个代理对象替代原先的target对象。

我们再次结合(Executor)interceptorChain.pluginAll(executor)这个语句来看,这个语句内部对executor执行了多次plugin,第一次plugin后通过Plugin.wrap方法生成了第一个代理类,姑且就叫executorProxy1,这个代理类的target属性是该executor对象。第二次plugin后通过Plugin.wrap方法生成了第二个代理类,姑且叫executorProxy2,这个代理类的target属性是executorProxy1...这样通过每个代理类的target属性就构成了一个代理链(从最后一个executorProxyN往前查找,通过target属性可以找到最原始的executor类)。

六、代理链上的拦截

代理链生成后,对原始目标的方法调用都转移到代理者的invoke方法上来了。Plugin作为InvocationHandler的实现类,他的invoke方法是怎么样的呢?

比如MyBatis官网的例子,当Configuration调用newExecutor方法的时候,由于Executor接口的update(MappedStatement ms, Object parameter)方法被拦截器被截获。因此最终返回的是一个代理类Plugin,而不是Executor。这样调用方法的时候,如果是个代理类,那么会执行:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
    try {  
       Set<Method> methods = signatureMap.get(method.getDeclaringClass());  
        if(methods != null && methods.contains(method)) {  
           // 调用代理类所属拦截器的intercept方法,  
           return interceptor.intercept(new Invocation(target, method, args));  
        }  
        return method.invoke(target, args);  
    } catch(Exception e) {  
        throw ExceptionUtil.unwrapThrowable(e);  
    }  
}

没错,如果找到对应的方法被代理之后,那么会执行Interceptor接口的interceptor方法。在invoke里,如果方法签名和拦截中的签名一致,就调用拦截器的拦截方法。我们看到传递给拦截器的是一个Invocation对象,这个对象是什么样子的,他的功能又是什么呢?

public class Invocation {  
  
    private Object target;  
    private Method method;  
    private Object[] args;  
   
    public Invocation(Object target, Method method, Object[] args) {  
        this.target =target;  
        this.method =method;  
        this.args =args;  
    }  
    ...  
  
    public Object proceed() throws InvocationTargetException, IllegalAccessException {  
        return method.invoke(target, args);  
    }  
} 

可以看到,Invocation类保存了代理对象的目标类,执行的目标类方法以及传递给它的参数。

在每个拦截器的intercept方法内,最后一个语句一定是return invocation.proceed()(不这么做的话拦截器链就断了,你的mybatis基本上就不能正常工作了)。invocation.proceed()只是简单的调用了下target的对应方法,如果target还是个代理,就又回到了上面的Plugin.invoke方法了。这样就形成了拦截器的调用链推进。

public Object intercept(Invocation invocation) throws Throwable {  
    //完成代理类本身的逻辑  
    ...
    //通过invocation.proceed()方法完成调用链的推进
    return invocation.proceed();
}

七、总结

MyBatis拦截器接口提供的3个方法中,plugin方法用于某些处理器(Handler)的构建过程。interceptor方法用于处理代理类的执行。setProperties方法用于拦截器属性的设置。其实MyBatis官网提供的使用 @Interceptors和 @Signature注解以及Plugin类这样处理拦截器的方法,我们不一定要直接这样使用。我们也可以抛弃这3个类,直接在plugin方法内部根据target实例的类型做相应的操作。总体来说MyBatis拦截器还是很简单的,拦截器本身不需要太多的知识点,但是学习拦截器需要对MyBatis中的各个接口很熟悉,因为拦截器涉及到了各个接口的知识点。

我们假设在MyBatis配置了一个插件,在运行时会发生什么?

  • 所有可能被拦截的处理类都会生成一个代理。
  • 处理类代理在执行对应方法时,判断要不要执行插件中的拦截方法。
  • 执行插接中的拦截方法后,推进目标的执行。
  • 如果有N个插件,就有N个代理,每个代理都要执行上面的逻辑。这里面的层层代理要多次生成动态代理,是比较影响性能的。虽然能指定插件拦截的位置,但这个是在执行方法时动态判断,初始化的时候就是简单的把插件包装到了所有可以拦截的地方。

因此,在编写插件时需注意以下几个原则

  • 不编写不必要的插件;
  • 实现plugin方法时判断一下目标类型,是本插件要拦截的对象才执行Plugin.wrap方法,否者直接返回目标本省,这样可以减少目标被代理的次数。

博文参考

MyBatis详解 - 插件机制 | Java 全栈知识体系​​​​​​​

https://www.cnblogs.com/lawsssscat/p/12695425.html#mybatis%E6%8B%A6%E6%88%AA%E5%99%A8%E6%8F%92%E4%BB%B6文章来源地址https://www.toymoban.com/news/detail-688344.html

到了这里,关于MyBatis——MyBatis插件原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java后端开发——Mybatis实验

    1.创建工程 在Eclipse中,创建名称为mybatis的工程 2.引入相关依赖 3.数据库准备 4.编写数据库连接信息配置文件 在项目的src目录下创建数据库连接的配置文件,这里将其命名为db.properties,在该文件中配置数据库连接的参数。 5.创建POJO实体 在项目的src/main/java目录下创建com.java

    2024年01月24日
    浏览(41)
  • 【计算机编程语言】JAVA-MyBatis(Eclipse)

    官网:https://mybatis.org/mybatis-3/zh/index.html 环境: JDK1.8(尽量) MySQL - 5.7(超经典) maven - 3.6.3 Eclipse 回顾: JDBC MySQL Java基础 maven Junit 框架:配置文件、最好的方式:官网文档 SSM框架:Spring SpringMVC Mybatis 1.1什么是Mybatis [外链图片转存失败,源站可能有防盗链机制,建议将图片保

    2024年02月16日
    浏览(30)
  • MyBatis第八讲:MyBatis插件机制详解与实战

    MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能。那么拦截器拦截MyBatis中的哪些内容呢?本文是MyBatis第8讲,对MyBatis插件机制详解。

    2024年02月13日
    浏览(45)
  • MyBatis 03 -MyBatis动态SQL与分页插件

    MyBatis的映射文件中支持在基础SQL上添加一些逻辑操作,并动态拼接成完整的SQL之后再执行,以达到SQL复用、简化编程的效果。 动态查询 where标签和if标签组合使用 动态修改 1.1 sql sql标签的作用是提取公共的sql代码片段 sql id属性:唯一标识 include refid属性:参照id 动态查询

    2023年04月15日
    浏览(32)
  • SSM框架的学习与应用(Spring + Spring MVC + MyBatis)-Java EE企业级应用开发学习记录(第五天)MyBatis的注解开发

    ​ 昨天我们深入学习了 MyBatis多表之间的关联映射,了解掌握了一对一关联映射,一对多关联映射,嵌套查询方式以及嵌套结果方式,掌握了缓存机制的一级缓存,二级缓存等概念,也使用了代码进行复现理解 。但是都是基于XML配置文件的方式来实现的,现在我们要学习一下

    2024年02月11日
    浏览(43)
  • IntelliJ IDEA安装Mybatis 插件Free Mybatis plugin

    需求描述 在开发一些Mybatis的项目,经常需要写一个Mapper接口,在找代码过程,经常需要去找对应的xml文件,所以非常的不方便。自从有了免费的free-mybatis-plugin插件之后 ,在可以实现在idea里一键跳转到对应的xml文件,反之,在xml文件也可以一键跳转到对应mapper接口。 也有其

    2024年02月15日
    浏览(100)
  • MyBatis Plus 插件 动态数据源实现原理与源码讲解 (dynamic-datasource-spring-boot-starter-master)

    目录 1. 介绍 2. 基本原理 3. 源码介绍 3.1 使用 AOP 拦截,方法执行前获取到当前方法要用的数据源 3.2 实现自定义 DataSource 接口,实现 DataSource 接口的 getConnect 方法做动态处理 多数据源即一个项目中同时存在多个不同的数据库连接池。 比如 127.0.0.1:3306/test   127.0.0.1:3307/test 

    2024年02月07日
    浏览(29)
  • SpringBoot(整合MyBatis + MyBatis-Plus + MyBatisX插件使用)

    1.需求分析 2.数据库表设计 3.数据库环境配置 1.新建maven项目 2.pom.xml 引入依赖 3.application.yml 配置数据源 数据库名 用户名 密码 驱动是mysql8的(因为上面使用了版本仲裁) 4.Application.java 编写启动类 5.测试 6.配置类切换druid数据源 7.测试数据源是否成功切换 4.Mybatis基础配置 1

    2024年03月20日
    浏览(45)
  • Java企业级开发学习笔记(2.1)MyBatis实现简单查询

    零、创建数据库与表 在Navicat里创建MySQL数据库testdb 创建用户表 - t_user CREATE TABLE t_user ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(50) DEFAULT NULL, age int(11) DEFAULT NULL, address varchar(255) DEFAULT NULL, PRIMARY KEY ( id ) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; 执行SQL语句来创建用户表 INSERT IN

    2024年02月09日
    浏览(76)
  • SSM框架的学习与应用(Spring + Spring MVC + MyBatis)-Java EE企业级应用开发学习记录(第一天)Mybatis的学习

    一、当前的主流框架介绍(这就是后期我会发出来的框架学习) Spring框架 ​ Spring是一个开源框架,是为了解决企业应用程序开发复杂性而创建的 ,其主要优势之一就是分层架构。 Spring提供了更完善的开发环境,可以为POJO(Plain Ordinary Java Object,普通Java对象 )对象提供企业级

    2024年02月12日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包