42.Mybatis动态SQL注解、代理模式

本文介绍了Mybatis使用注解进行动态SQL构建的三种方式,包括自行拼接SQL、使用Mybatis SQL类以及直接应用增删改查注解。接着详细探讨了代理模式,包括静态代理和动态代理的差异,特别是JDK和CGLIB动态代理的实现。此外,还展示了如何结合自定义注解和动态代理实现业务层的事务控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

mybatis注解开发

@InsertProvider

@DeleteProvider

@UpdateProvider

@SelectProvider

以上四个注解用于使用SQL构建器来完成动态SQL的组建。

注解中有两个属性:type和method

type:用于构建SQL语句的类型的字节码对象

method:构建SQL语句的方法名称

案例:同时查询多个id值的用户对象

动态SQL的三种实现方式(注解实现动态sql)
方式一:自己拼接SQL语句

sql构建器

public class SqlBuilder {
    public String getSelectUserSql(@Param("ids") List<Integer> ids){
        String sql="select * from t_user where id in(";
        for (int i = 0; i < ids.size(); i++) {
            if(i<ids.size()-1) {
                sql += ids.get(i) + ",";
            }else {
                sql += ids.get(i) + ")";
            }
        }
        return sql;
    }
}

mapper层

	@SelectProvider(type =/*产生SQL语句的类的字节码对象*/SqlBuilder.class,method =/*产生SQL语句的方法名称*/"getSelectUserSql")
    List<User> findByIds(@Param("ids") List<Integer> ids);

测试类

	public static void main(String[] args) {
        UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
        List<User> users = mapper.findByIds(Arrays.asList(1, 2, 3, 4, 5));
        users.forEach(user->{
            System.out.println(user.getUsername());
        });
    }
方式二:使用mybatis提供的SQL类来完成动态SQL的创建
package com.woniuxy.utils;

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.jdbc.SQL;

import java.util.List;

public class SqlBuilder {
    public String getSelectUserSql(@Param("ids") List<Integer> ids){

        //生成动态SQL的方式二
        String condition="id in (";
        for (int i = 0; i < ids.size(); i++) {
            if(i<ids.size()-1) {
                condition += ids.get(i) + ",";
            }else {
                condition += ids.get(i) + ")";
            }
        }
        String sql = new SQL().SELECT("*")// SELECT *
                .FROM("t_user")// from t_user
                .WHERE(condition).toString();

        return sql;
    }
}

方式三:不使用provider,直接使用增删改查注解完成SQL语句的执行

注意:此处不再使用@SelectProvider,而是使用@Select注解完成动态SQL的执行,将XML中的动态SQL写到<script>标签中即可。

@Select("<script>" +
            "select * from t_user where id in" +
            " <foreach collection='ids' item='id' open='(' close=')' separator=','>" +
            "   #{id}" +
            " </foreach>" +
            "</script>")
    List<User> findByIds(@Param("ids") List<Integer> ids);
代理模式

java:23种设计模式之一。

作用:为某个对象提供一种对象来控制对该对象访问。

代理模式的区别:主要是基于代理对象的生成时机来说的。

静态代理:在编译期,代理对象就已经生成了。

动态代理: 程序运行期间,动态生成代理对象。

不使用代理:

业务层接口

package com.woniuxy.service;

/**
 * 业务层接口
 */
public interface UserService {

    //新增
    void add();
    //删除
    void delete();
    //修改
    void update();
    //查询
    void find();

}

业务层实现类

package com.woniuxy.service.impl;

import com.woniuxy.service.UserService;

public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("执行新增操作");
    }
    @Override
    public void delete() {
        System.out.println("执行删除操作");
    }
    @Override
    public void update() {
        System.out.println("执行修改操作");
        System.out.println(1/0);
    }
    @Override
    public void find() {
        System.out.println("执行查询操作");
    }
}
静态代理:

在编译期,代理对象就已经生成了。

package com.woniuxy.proxy.staticProxy;

import com.woniuxy.service.UserService;
import com.woniuxy.service.impl.UserServiceImpl;

/**
 * 静态代理类
 */
public class StaticProxyObject implements UserService {
    //在静态代理类中,创建真实对象的对象
    private UserServiceImpl userServiceImpl=new UserServiceImpl();

    @Override
    public void add() {
        System.out.println("开启事务");
        try {
            //调用真实对象方法
            userServiceImpl.add();
            System.out.println("提交事务");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("事务回滚");
        }
    }

    @Override
    public void delete() {
        System.out.println("开启事务");
        try {
            userServiceImpl.delete();
            System.out.println("提交事务");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("事务回滚");
        }
    }

    @Override
    public void update() {
        System.out.println("开启事务");
        try {
            userServiceImpl.update();
            System.out.println("提交事务");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("事务回滚");
        }
    }

    @Override
    public void find() {
        
    }
}

测试

//创建静态代理对象
StaticProxyObject staticProxyObject = new StaticProxyObject();
//通过静态代理对象调用方法
staticProxyObject.add();
staticProxyObject.delete();
staticProxyObject.update();

​ 使用静态代理的虽然可以消除业务层中非业务代码,让业务层保持纯粹性,但实现代理并没有减少,且每个代理类只能代理一个业务层实现类,反而增加了更多的类,因此一般没人用。

动态代理(重点):

在程序运行期间,动态生成代理对象。

动态代理的两种实现

JDK动态代理:

基于接口的实现。真实类型必须有接口,才可以使用JDK动态代理

要使用jdk动态代理,必须实现InvocationHandler接口,还需要使用Proxy类的相应API来完成代理对象的创建。

1、创建invocationHandler

/**
 * jdk动态代理
 */
public class DynamicProxyUtil<T> implements InvocationHandler {
    private T t;//真实对象
    /**
     * 获取代理对象
     * @param real 真实对象
     * @return
     */
    public T getProxyObject(T real){
        t=real;
        //newProxyInstance(参数1,参数2,参数3)
        //参数1:真实对象的类加载器
        //参数2:真实对象所实现的接口数组
        //参数3:invocationHandler
        return (T)Proxy.newProxyInstance(real.getClass().getClassLoader(),
                real.getClass().getInterfaces(),this);//用于生成代理对象
    }
    /**
     *
     * @param proxy
     * @param method   :method其实就是真实对象中某个方法的方法对象
     * @param args     :就是真实对象的某个方法的参数列表
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object returnValue = null;
        System.out.println("开启事务");
        try {
            returnValue = method.invoke(/*真实对象*/t, /*真实对象参数列表*/args);//调用了真实对象的方法
            System.out.println("提交事务");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("事务回滚");
        }
        return returnValue;
    }
}

2、测试

//创建代理对象
UserService userService = new DynamicProxyUtil<UserService>().getProxyObject(new UserServiceImpl());
//通过代理对象调用方法
userService.add();
userService.delete();
userService.update();

图示

image-20210908153055146

CGLIB动态代理:

基于继承的实现,生成代理对象是以真实对象的子类的形式出现的。

实现步骤

1、导入 cglib-3.3.0.jar asm-7.1.jar

2、创建方法拦截器(创建类,实现MethodInterceptor接口)

/**
 * cglib动态代理实现
 */
public class CglibDynamicProxyUtil implements MethodInterceptor {

    /**
     *
     * @param clazz   :真实对象的字节码对象
     * @param <T>
     * @return
     */
    public <T>T getProxyObject(Class<T> clazz){
        //使用Enhancer对象来完成代理对象的相关配置并创建代理对象
        Enhancer enhancer = new Enhancer();
        //设置代理对象的父类类型
        enhancer.setSuperclass(clazz);
        //设置方法拦截器
        enhancer.setCallback(this);
        //创建代理对象
        return (T)enhancer.create();
    }

    /**
     * @param obj              :真实对象(业务层接口)
     * @param method
     * @param args        :真实对象方法的参数列表
     * @param methodProxy    :真实对象某个方法的代理对象
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object returnValue =null;
        System.out.println("开启事务");
        try {
             returnValue=methodProxy.invokeSuper(obj, args);
            System.out.println("提交事务");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("事务回滚");
        }
        return returnValue;
    }
}

3、测试

//创建代理对象
UserServiceImpl userServiceImpl = new CglibDynamicProxyUtil().getProxyObject(UserServiceImpl.class);
//通过代理对象调用方法
userServiceImpl.add();
userServiceImpl.delete();
userServiceImpl.update();
使用动态代理+自定义注解完成业务层指定方法的事务控制

案例:完成转账操作

自定义注解:如果业务层方法需要有事务,则在对应的方法上添加该注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Transactional {
}

数据访问层

public interface AccountMapper {

    /**
     * 根据用户名查询账户信息
     */
    @Select("select * from account where username=#{username}")
    Account findByUsername(String username);

    /**
     * 根据用户名去修改账户余额
     * @param account
     * @return
     */
    @Update("update account set balance=#{balance} where username=#{username}")
    int updateAccount(Account account);

}

业务逻辑层

接口

public interface AccountService {
    /**
     * 执行转账的业务
     * @return
     */
    @Transactional
    void transfer(String source,String target,Double money);
}

实现类:完成转账操作,需要查询和修改,会在业务层中调用数据访问层来实现对应的数据库操作

public class AccountServiceImpl implements AccountService {


    private AccountMapper accountMapper;

    public AccountServiceImpl(){
        accountMapper= MybatisUtil.getMapper(AccountMapper.class);
    }


    /**
     * 执行转账操作
     * @param source
     * @param target
     * @param money
     */
    @Override
    public void transfer(String source, String target, Double money) {

        //先查询
        Account sourceAccount = accountMapper.findByUsername(source);//转出的账户
        Account targetAccount = accountMapper.findByUsername(target);//转入的账户

        if (sourceAccount.getBalance()>=money) {//如果余额大于要转出的金额,则执行转账操作
            //后修改
            sourceAccount.setBalance(sourceAccount.getBalance()-money);
            targetAccount.setBalance(targetAccount.getBalance()+money);

            accountMapper.updateAccount(sourceAccount);
            System.out.println(1/0);
            accountMapper.updateAccount(targetAccount);

        }else{
            throw new RuntimeException("余额不足");
        }

    }
}

动态代理工具类:在invoke()中,通过判断Method对象上是否存在对应的注解,来决定是否使用事务。

public class JdkDynamicProxyUtil<T> implements InvocationHandler {


    private T t;


    public T getProxyObject(T real){
        t=real;
        return (T)Proxy.newProxyInstance(real.getClass().getClassLoader(),
                real.getClass().getInterfaces(),this);
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object returnValue=null;
        if (method.isAnnotationPresent(Transactional.class)) {
            //真实对象的某个方法的方法对象上如果有对应的注解,则进行事务处理
            try {
                returnValue=method.invoke(t,args);
                MybatisUtil.getSqlSession().commit();
            } catch (Exception e) {
                e.printStackTrace();
                MybatisUtil.getSqlSession().rollback();
            }
        }else {//没有对应的注解,则不进行事务处理
            returnValue=method.invoke(t,args);
        }
        return returnValue;
    }
}

mybatis的工具类:该工具类中的Sqlsession应该确保是一个唯一的值。

public class MybatisUtil {
    private static SqlSession sqlSession;
    static {
        //try  with  resources
        try(InputStream is = Resources.getResourceAsStream("sqlConfig.xml");){
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
            sqlSession = factory.openSession();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static <T>T getMapper(Class<T> clazz){
        return sqlSession.getMapper(clazz);
    }


    public static SqlSession getSqlSession(){
        return sqlSession;
    }

}

测试

//创建代理对象
AccountService accountService = new JdkDynamicProxyUtil<AccountService>().getProxyObject(new AccountServiceImpl());
//通过代理对象调用方法
accountService.transfer("jack","tom",1000.00);
        sqlSession = factory.openSession();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public static <T>T getMapper(Class<T> clazz){
    return sqlSession.getMapper(clazz);
}


public static SqlSession getSqlSession(){
    return sqlSession;
}

}


测试

```java
//创建代理对象
AccountService accountService = new JdkDynamicProxyUtil<AccountService>().getProxyObject(new AccountServiceImpl());
//通过代理对象调用方法
accountService.transfer("jack","tom",1000.00);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值