mybatis的Mapper使用自定义注解

一般项目都会区分读写库或者多数据源
这就导致不同的查询需要去不同的库查询
我们公司的多数据源采用的是注解的形式实现的
举例:
@ReadDataSource注解去读库
@WriteDataSource注解去写库
(注解的实现类里做的切换数据源的操作)
使用spring的jdbctemplete再持久层方法上加注解是可以实现数据源切换的
例如:

    @ReadDataSource
	public List<Map<String, Object>> get() {
		String SQL ="SELECT * FROM Demo";
		List<Map<String, Object>> retMap = null;
		try {
			retMap = jdbcTemplate.queryForList(SQL);
		} catch (DataAccessException e) {
			return new ArrayList<Map<String, Object>>();
		}
		return retMap;
	}

当再服务处调用这个方法是可以实现(本类中调用的话 需要拿到spring的对象调用才可以,直接调用是不行的,因为本类中调用方法不会走代理)

但是使用mybatis的话 再mapper上加注解是无效的,原因是调用mapper的方法时,mybatis会通过代理实现这个方法,但是并不会实现方法上的注解(除了mybatis自带的@select等注解)这种情况下切换数据源是无效的
举例:

    public interface DemoMapper {

        @ReadDataSource
        List<Demo> get();

        @WriteDataSource
        void insert(Demo demo);

	}

最终的实现方案是通过mybatis拦截器实现的,拦截器如下:

    //拦截Executor的三个方法
@Intercepts({@Signature(type = Executor.class,method = "query", args = { MappedStatement.class, Object.class,
        RowBounds.class, ResultHandler.class }),
        @Signature(type = Executor.class,method = "query", args = { MappedStatement.class, Object.class,
        RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
        @Signature(type = Executor.class,method = "update", args = { MappedStatement.class, Object.class})})
@Component
public class DataSourceInterceptor implements Interceptor {

    DataSourceEntity entity;

    public void init(DataSourceEntity entity) {
        //项目启动的时候调用会把非默认数据源放这里
        this.entity = entity;
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement)args[0];
        //拿到方法全名  全类名.方法名
        String fullMethodName = ms.getId();
        //拿到全类名
        String className = getClassName(fullMethodName);
        Class<?> mapper = Class.forName(className);
        //因为我没有拿到方法的参数,所以要保证方法名不同(重载的方法数据源也应该是一样的)
        Method methodByMethodName = getMethodByMethodName(getMethodName(fullMethodName), mapper.getMethods());
        //拿到方法上的数据源切换注解
        WriteDataSource writeDataSource = methodByMethodName.getAnnotation(WriteDataSource.class);
        ReadDataSource readDataSource = methodByMethodName.getAnnotation(ReadDataSource.class);
        boolean flag = false;
        if(readDataSource!=null){
            readBefore();
            flag = true;
        }
        if(writeDataSource!=null){
            writeBefore();
            flag = true;
        }
        //执行数据查询
        Object proceed = invocation.proceed();
        if(flag){
            after();
        }
        //返回数据
        return proceed;
    }

    private String getClassName(String fullMethodName){
        return fullMethodName.substring(0,fullMethodName.lastIndexOf("."));
    }

    private String getMethodName(String fullMethodName){
        return fullMethodName.substring(fullMethodName.lastIndexOf(".")+1);
    }

    private Method getMethodByMethodName(String methodName,Method[] methods){
        for (Method method : methods) {
            if(method.getName().equals(methodName)){
                return method;
            }
        }
        return null;
    }

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

    @Override
    public void setProperties(Properties properties) {

    }

    public void writeBefore()  {
        //先清空数据源
        DataSourceHolder.clearDataSource();
        //设置写库
        DataSourceBeanBuilder builder = new 		DataSourceBeanBuilder(entity.getDbName() + DynamicDataSourceGlobal.W, entity.getIpMaster(), entity.getPort(), entity.getDbName(), entity.getUsername(), entity.getPassword());
        DataSourceHolder.setDataSource(builder);
    }

    public void readBefore()  {
        //先清空数据源
        DataSourceHolder.clearDataSource();
        //设置读库
        DataSourceBeanBuilder builder = new DataSourceBeanBuilder(entity.getDbName() + DynamicDataSourceGlobal.R, 	entity.getIpMaster(), entity.getPort(), entity.getDbName(), entity.getUsername(), entity.getPassword());
        DataSourceHolder.setDataSource(builder);
    }

    public void after(){
        //清空数据源
        DataSourceHolder.clearDataSource();
        DynamicDataSourceHolder.clearDataSource();
    }
}
	//设置mybatis的SqlSessionFactory的时候注入拦截器
	@Bean
    public SqlSessionFactory sqlSessionFactory(DynamicDataSource dataSource,DataSourceInterceptor dataSourceInterceptor) throws Exception {
        SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
        sqlSessionFactory.setDataSource(dataSource);
        sqlSessionFactory.setPlugins(new Interceptor[]{dataSourceInterceptor});
        return sqlSessionFactory.getObject();
    }
    
	//项目启动最后将数据源放到拦截器里
    @Bean
    public CommandLineRunner initData(DataSourceDao dataSourceDao,DataSourceInterceptor dataSourceInterceptor) {
        return strings -> {
            try { 
                dataSourceInterceptor.init(dataSourceDao.getDataSourceByTenantName("1"));
            } catch (Exception e) {
                e.printStackTrace();
            }
        };
    }
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Mybatis-plus提供了多种自定义SQL的方式,以下是其中几种常用的方式: 1. 使用@Select注解或者Mapper.xml文件中的<select>标签编写自定义SQL语句。 2. 使用@Update、@Insert、@Delete注解或者Mapper.xml文件中的<update>、<insert>、<delete>标签编写自定义SQL语句。 3. 使用Wrapper对象构建查询条件,然后调用BaseMapper的selectList()、selectOne()等方法进行查询。 4. 使用SqlHelper类提供的方法构建SQL语句,然后调用BaseMapper的selectList()、selectOne()等方法进行查询。 5. 使用定义的SQL解析器,将自定义SQL语句解析成Mybatis-plus可识别的SQL语句,然后调用BaseMapper的selectList()、selectOne()等方法进行查询。 以上是常用的几种自定义SQL的方式,具体使用哪种方式取决于具体的业务需求和个人习惯。 ### 回答2: Mybatis-plus 是一个基于 Mybatis 的增强工具,它提供了一系列解决方案,使得 Mybatis使用变得更加简单和高效。其中,自定义 SQL 是 Mybatis-plus 的一个重要特性,它提供了一种自由扩展 SQL 的方式,使得开发者可以通过自己的实现来满足各种灵活的需求。 要使用定义 SQL,首先需要在 Mapper 接口中定义方法,并用 @Select 注解标注该方法。在注解中,我们可以书写 SQL 语句,如: ``` @Select("select * from user where name = #{name}") User findUserByName(@Param("name") String name); ``` 这里的 @Select 注解告诉了 Mybatis-plus,这是一个查询方法,所使用的 SQL 是 select * from user where name = #{name}。其中,#{name} 是一个占位符,Mybatis-plus 会自动将方法参数中的 name 值替换到该占位符中。 除了 @Select,Mybatis-plus 还提供了一些其他的注解,如 @Insert、@Update、@Delete 等,它们分别表示插入、更新、删除操作。类似于 @Select,这些注解也可以用自定义 SQL,声明方式都类似。 在定义完自定义 SQL 方法之后,我们就可以在业务方法中调用这些方法来实现对数据库的操作了,如: ``` User user = userMapper.findUserByName("Tom"); ``` 最后需要注意的是,虽然自定义 SQL 可以满足我们的各种需求,但是它们本身也存在一些缺点。比如它们可能会使代码可读性变低,也会存在 SQL 注入等安全问题。因此,在使用定义 SQL 的时候,需要谨慎考虑,遵循安全和可读性原则来编写。 ### 回答3: MyBatis-Plus 是一个基于 MyBatis 的增强工具,在其基础上,提供了更加强大和便捷的操作。在使用 MyBatis-Plus 进行数据库操作时,很多情况下需要编写自定义的 SQL 语句来满足特定的需求,因此,学习如何自定义 SQL 语句是非常必要的。 MyBatis-Plus 支持多种方式自定义 SQL,以下分别介绍: 1. @Select 注解 @Select 注解可以在方法上加入 SQL 语句,执行时会使用该 SQL 语句进行查询。例如: @Select("select * from user where id = #{id}") User selectById(Long id); 2. XML 映射文件 在 MyBatis-Plus 配置文件中,可以配置 XML 映射文件的位置和名称,然后在 XML 文件中书写 SQL 语句,例如: <select id="selectById" resultType="com.example.model.User"> select * from user where id = #{id} </select> 3. 自定义接口 自定义接口是将 SQL 语句封装在方法中最为常用的方式,只需定义一个接口并继承 MyBatis-Plus 提供的 BaseMapper 接口,然后在接口中定义方法即可。例如: public interface UserMapper extends BaseMapper<User> { @Select("select * from user where username = #{username}") User selectByUsername(String username); } 4. 自定义 SQL 语句构建器 MyBatis-Plus 提供了 LambdaQueryWrapper 和 QueryWrapper 等构建器来方便编写 SQL 语句,若这些构建器无法满足我们的需求,可以自定义 SQL 语句构建器。例如: public class CustomWrapper<T> extends AbstractWrapper<T, CustomWrapper<T>> { public CustomWrapper() { super(); } public CustomWrapper(T entity) { super.setEntity(entity); } public CustomWrapper(T entity, String... columns) { super.setEntity(entity); super.select(columns); } public CustomWrapper<T> myMethod(String field, Object value) { super.eq(field, value); return this; } } 以上就是 MyBatis-Plus 自定义 SQL 的几种方式,选择哪种方式取决于个人需求和习惯。无论是哪种方式,都需要注意 SQL 注入的风险,保证 SQL 语句的安全性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值