AOP动态切换数据源和org. Apache. ibatis. binding. BindingException:Invalid bound statement (not found)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.5.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.hskw</groupId>
	<artifactId>shard</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>shard</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.1.14</version>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.2</version>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>
slave:
  datasource:
#   数据源基本配置
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.xxx.xxx:3306/sjk1
    type: com.alibaba.druid.pool.DruidDataSource
#   数据源其他配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
master:
  datasource:
#   数据源基本配置
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.xxx.xxx:3306/sjk2
    type: com.alibaba.druid.pool.DruidDataSource
#   数据源其他配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
package com.luban.sharding.annotation;

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.METHOD)
public @interface MyDataSource {

    String value() default "master";

}



package com.luban.sharding.utils;

public class DbUtil {
    
    public static String master="master";
    public static String slave="slave";

    private static ThreadLocal<String> threadLocal=new ThreadLocal();


    public static void setDb(String db){
        threadLocal.set(db);
    }

    public static String getDb(){
        return threadLocal.get();
    }

}



package com.luban.sharding.factorybean;

import com.luban.sharding.utils.DbUtil;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;


public class DbFactoryBean implements FactoryBean<AbstractRoutingDataSource> {

    @Autowired
    @Qualifier("masterDataSource")
    private DataSource masterDataSource;

    @Qualifier("slaveDataSource")
    @Autowired
    private DataSource slaveDataSource;

    @Nullable
    @Override
    public AbstractRoutingDataSource getObject() throws Exception {
        AbstractRoutingDataSource abstractRoutingDataSource=new AbstractRoutingDataSource() {
            @Nullable
            @Override
            protected Object determineCurrentLookupKey() {
                System.out.println(DbUtil.getDb());
                return DbUtil.getDb();
            }
        };

        abstractRoutingDataSource.setDefaultTargetDataSource(masterDataSource);
        Map<Object,Object> map=new HashMap<>();
        map.put("master",masterDataSource);
        map.put("slave",slaveDataSource);
        abstractRoutingDataSource.setTargetDataSources(map);
        abstractRoutingDataSource.afterPropertiesSet();
        return abstractRoutingDataSource;
    }

    @Nullable
    @Override
    public Class<?> getObjectType() {
        return AbstractRoutingDataSource.class;
    }
}



package com.luban.sharding.config;

import com.luban.sharding.factorybean.DbFactoryBean;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;


public class DbConfig implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata,
                                        BeanDefinitionRegistry beanDefinitionRegistry) {
        //注册MyMapperFactoryBean
        //为什么不用其他方式注册MyMapperFactoryBean呢    因为这里是代码形式,可以循环,产生多个
        BeanDefinitionBuilder beanDefinitionBuilder =
                BeanDefinitionBuilder.genericBeanDefinition(DbFactoryBean.class);
        AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
        //注册
        beanDefinitionRegistry.registerBeanDefinition("dbFactoryBean",beanDefinition);
    }

}




package com.luban.sharding.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.luban.sharding.factorybean.DbFactoryBean;
import com.luban.sharding.utils.DbUtil;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Import(DbConfig.class)
@Configuration
@MapperScan("com.luban.sharding.mapper")
public class DruidConfig {

    @ConfigurationProperties(prefix = "master.datasource")
    @Bean
    public DataSource masterDataSource(){
        return  new DruidDataSource();
    }

    /**
     * 事物管理器,单纯的读是不用开启事物的,
     * 写数据源需要开启事务
     * @param dbFactoryBean
     * @return
     */
    @Bean
    public PlatformTransactionManager txManager(DataSource dbFactoryBean) {
        return new DataSourceTransactionManager(dbFactoryBean);
    }


    @ConfigurationProperties(prefix = "slave.datasource")
    @Bean
    public DataSource slaveDataSource(){
        return  new DruidDataSource();
    }


//    @Bean
//    public DynamicDataSource dynamicDataSource(){
//        DynamicDataSource dynamicDataSource=new DynamicDataSource();
//        Map<Object,Object> map=new HashMap<>();
//        map.put(DbUtil.master,masterDataSource());
//        map.put(DbUtil.slave,slaveDataSource());
//        dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
//        dynamicDataSource.setTargetDataSources(map);
//        return dynamicDataSource;
//    }


    @Bean
   public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dbFactoryBean){
       SqlSessionFactoryBean sqlSessionFactory=new SqlSessionFactoryBean();
       sqlSessionFactory.setDataSource(dbFactoryBean);
       sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/*.xml"));
       return sqlSessionFactory;
   }

   @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactoryBean sqlSessionFactoryBean) throws Exception {
       SqlSessionTemplate sqlSessionTemplate=new SqlSessionTemplate(sqlSessionFactoryBean.getObject());
       return sqlSessionTemplate;
   }

}



package com.luban.sharding.aop;

import com.luban.sharding.annotation.MyDataSource;
import com.luban.sharding.utils.DbUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
@Aspect
public class DbAop {

    @Before(value = "execution(* com.luban.sharding.mapper..*.*(..))")
    public void before(JoinPoint joinPoint){
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        MyDataSource annotation = method.getAnnotation(MyDataSource.class);
        if(annotation==null){
            DbUtil.setDb(DbUtil.master);
           return;
        }
        System.out.println(annotation.value()+"---------------");
        DbUtil.setDb(annotation.value());
    }

}




package com.luban.sharding;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@EnableAspectJAutoProxy(proxyTargetClass = true)
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class ShardingApplication {

	public static void main(String[] args) {
		SpringApplication.run(ShardingApplication.class, args);
	}

}




package com.luban.sharding.mapper;

import com.luban.sharding.annotation.MyDataSource;
import com.luban.sharding.entity.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface UserMapper {


    @Insert({"insert into user(id,name) values(#{sid},#{sname})"})
    Integer insertUser(User user);


    @MyDataSource("slave")
    @Select("select id as sid,name as sname from user")
    List<User> selectUser();
}

到此,想要切换数据源,就在mapper方法上加上自定义注解,指定数据源的值,因为指定的AOP扫描mapper文件。

这是利用FactoryBean的方式,然后import引入。还有一种就是用@Bean的方式,两种的区别就是,@Bean走完整的Spring的生命周期流程,FactoryBean特殊处理,这个得去看Spring源码。

去掉FacoryBean和DbConfig类,然后去掉DruidConfig类上的@Import注解,新建DynamicDataSource类,打开DruidConfig类中

注释的DynamicDataSource这个@Bean。

package com.luban.sharding.config;

import com.luban.sharding.utils.DbUtil;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.lang.Nullable;

public class DynamicDataSource extends AbstractRoutingDataSource{
    @Nullable
    @Override
    protected Object determineCurrentLookupKey() {
        System.out.println(DbUtil.getDb());
        return DbUtil.getDb();
    }
}

org. Apache. ibatis. binding. BindingException:Invalid bound statement (not found)

这个错,首先是看就网上其他博客说的那样,你有没有拼错、不对应、全路径报名等。我的不是,拼错这些除了新手一般不会这样,我的是这么回事,mapper和xml不在同一文件夹下,所以配置文件中指定了mybatis.mapper-locations=classpath:mybatis/mapper/*.xml,但是动态数据源,SpringBoot的自动配置会失效,所以报找不到mapper也就是上面那个错,你得利用Java代码配置,上面有我就不在贴一次代码了。还有就是new PathMatchingResourcePatternResolver()的时候,打了前几个字母,Idea不提示,你不用管,继续敲到最后几个的时候就会提示。

发布了65 篇原创文章 · 获赞 88 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/hskw444273663/article/details/103832207