mybatis动态代理

动态SQL

采用OGNL表达式来进行SQL的淘汰不需要的元素。根据不同的添加进行动态的SQL的组装和拼接

if关键字

if标签:包含test表达式 必填,通过ognl表达式进行判断,true和false,
如果为真,会执行if中的SQL
如果为假,则不执行if中的SQL

where 标签

where标签作用:如果where标签包含了具有返回值时,就会插入一个where,如果where后面的字符串以and 和or开头的,则将他剔除

where和if联合使用

where表达式一般和if表达式一块使用,如果一个条件都不满足,则不拼接where到SQL中,如果有一个或者多个if表达式成立,where会直接拼接在SQL上,并且紧随where的表达式的and或者or会被忽略

<select id="selectClass" resultType="org.example.pojo.Class">
        select * from
    <where>
        <if test="c_id!=null">
           and c_id=#{c_id}
        </if>
        <if test="c_name!=c_name">
           and c_name=#{c_name}
        </if>
    </where>

如果传入c_id则SQL语句为: select * from wherec_id=#{c_id}
两个都传入则SQL语句为:select * from c_id=#{c_id}

set标签

set标签的作用,如果set标签中的元素有返回值,就插入一个set,如果set后的字符串以逗号为结尾,则将最后的逗号剔除
set标签和if表达式一块使用,如果有条件满足时,会直接拼接一个set到SQL上,并将最后一个SQL的逗号去掉

<update id="updateStudent" parameterType="student" >
        update student
        <set>
            <if test="Sname != null ">
                 Sname  = #{Sname} ,
            </if>
            <if test="Sage != null ">
                 Sage  = #{Sage} ,
            </if>
        </set>
        where SID = #{SID}
    </update>

trim标签

trim标签使用来去除SQL语句中多余的and关键字、逗号,或者给SQL拼接的where、set或者values等前后缀,可以选择性的进行插入、更显,或者添加查询操作
*prefix:前缀      
prefixoverride:去掉第一个and或者是or
suffix:后缀  
suffixoverride:去掉最后一个逗号(也可以是其他的标记,就像是上面前缀中的and一样)

<select id="selectStudentByDIY" parameterType="student" resultType="student">
        select * from Student
        <trim prefix="where" prefixOverrides="and">
            <if test="SID != null and SID != 0">
                and SID = #{SID}
            </if>
            <if test="Sname != null ">
                and Sname  = #{Sname}
            </if>
        </trim>
    </select>
    
       <update id="updateStudent" parameterType="student" >
        update student
        <trim prefix="set" suffixOverrides=",">
            <if test="Sname != null ">
                Sname  = #{Sname} ,
            </if>
            <if test="Sage != null ">
                Sage  = #{Sage} ,
            </if>
        </trim>
        where SID = #{SID}
    </update>  

foreach标签

批量处理
如:
insert table_name (id,name,age) values((1,1,1),(2,2,2))
select * from Student where SID in(1,2,3,4,5);

foreach标签实现批量数据处理,遍历集合对象
各属性解释

collection:必填,指定输入参数类型(列表:list  数组:array  hashmap:map)
item:起名字,给定集合中单个元素的名称
open:开始的字符串
close:结束的字符串
separator:数据之间的分隔符
index:索引的属性名,当类型为数组时是当前的索引值
 <select id="selectStudentByIds" resultType="student">
        select * from student where SID in
        <foreach collection="list" item="id"  open="(" close=")" separator="," index="">
            #{id}
        </foreach>
    </select>

动态代理

代理模式是设计模式之一
在这里插入图片描述
委托类(RealSubject):真正实现功能或者提供真实服务
代理类(Proxy):并不提供真正实现服务,而是通过调用委托类的对象的相关方法提供特定服务,可以在该服务基础上提供一些其他的服务
subject:代理类和委托类共有的接口或者服务,提供了代理类和委托类共有方法
Client需要的服务是是通过代理类来获取的,而真正的服务是有委托类提供的

JDK自带动态代理

接口:

/**
 * 代理类和委托类共有的接口
 */
public interface Iuser {
    void eate();
}

委托类:

/**
 * 委托类:具体实现接口中方法
 */
public class User implements Iuser{
    @Override
    public void eate() {
        System.out.println("User实现了eate方法");
    }
}

代理辅助类:

/**
 * 代理辅助类
 * 要实现动态代理,则需要给定一个实现类invocationHandler接口的实现类
 * 该类中需要持有IUser实现类的引用
 */
public class UserProxy implements InvocationHandler {
    private Iuser user;
    public UserProxy(Iuser user){
        this.user=user;
    }
    /**
     * 实现InvocationHandler接口
     * 重写invoke方法
     * @param proxy :产生的代理对象
     * @param method:调用的方法
     * @param args :表示方法的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理类自定义业务逻辑1");
        //调用委托类的方法
        method.invoke(user,args);
        System.out.println("代理类自定义业务逻辑2");
        return null;
    }

产生代理类的形式:

 public static void main(String[] args) {
        /**
         * ClassLoader loader,指定代理辅助类的加载器
         * Class<?>[] interfaces,当前被代理的接口
         * InvocationHandler h :代理辅助类
         */
        //产生的代理类
        Iuser user = (Iuser) Proxy.newProxyInstance(UserProxy.class.getClassLoader(), new Class[]{Iuser.class}, new UserProxy(new User()));
            user.eate();
    }

运行结果:
在这里插入图片描述
通过分析:动态打理的对象的产生通过Proxy.newProxyInstance()调用产生了代理对象
代理对象调用eate方法,通过打印结果可知,其调用了invocationHandler辅助类的invoke方法,该方法下来调用委托类User的eate实现

CGLib代理

JDK自带的动态代理使用比较简单,缺点也是明显的,需要目标对象实现一个或多个接口
当代理没有接口的类,此时Proxy和InvocationHandler机制就不能使用了,此时就需要使用cglib

cglib采用字节码技术,通过对字节码为一个类创建子类,并在子类中采用方法拦截的技术,拦截所有父类方法的调用,顺势织入横切技术

AOP技术的实现是使用到了JDK自带的动态代理和CGLib动态代理的

演示

引入依赖:

<dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.2</version>
        </dependency>

委托类:

public class CCGlibSuper {
    public void doing(){
        System.out.println("委托类");

    }
}

辅助类:

/**
 * 产生CGlib代理对象的辅助类
 * 实现MethodInterceptor
 * 该接口中的intercept方法需要实现
 */
public class CGLibProxy implements MethodInterceptor {
     private Enhancer enhancer = new Enhancer();
        public <T> T getProxy(Class<T> tClass){
            //设置父类
            enhancer.setSuperclass(tClass);
            enhancer.setCallback(this);
            //通过字节码技术创建子类实例
            return (T)enhancer.create();
        }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理类使用前");
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("代理类使用后");
        return o1;
    }

    public static void main(String[] args) {
        CGLibProxy cgLibProxy = new CGLibProxy();
        CCGlibSuper proxy = cgLibProxy.getProxy(CCGlibSuper.class);
        proxy.doing();
    }
}

MyBatis中的动态代理

MyBatis中使用的是 JDK自带动态代理模式
添加接口信息如下

configuration.addMapper(StudentMapper.class);//添加mapper

addMapper方法实现:
Configuration类:

  public <T> void addMapper(Class<T> type) {
    mapperRegistry.addMapper(type);
  }

mapper实际上被添加到了mapperRegistry中
MapperRegistry类:

  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();


public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) { //只添加接口
      if (hasMapper(type)) {//不能重复添加
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

mybatis在执行configuration.addMapper(StudentMapper.class),最终将接口信息放到HashMap中,名称为knownMappers,knownMappers是MapperRegistry类的私有属性,是一个HashMap,key为当前class对象,value为一个MapperProxyFactory类型的实例

继续看getMapper的代码跟踪

//通过动态代理机制(反射)产生了一个代理类(重点:动态代理模式,源码实现)
            StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

getMapper实现类通过debug测试确定为DefaultSQLSession类
DefaultSQLSession类:

  @Override
  public <T> T getMapper(Class<T> type) {
    return configuration.<T>getMapper(type, this);
  }

该代码直接调用Configuration类中的getMapper方法
Configuration类:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

通过代码是通过Proxy.newProxyInstance产生了一个StudentMapper接口的代理对象,mybatis为了完成mapper接口的实现运用了代理模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值