java spring boot mybatis multi-data source integration configuration

When doing a project, sometimes it is necessary to configure multiple data sources in a project. There are many online too. I will write a more complete and relatively elegant way here.

When using mybatis, you can use multiple DAOs. One DAO does it all

Go directly to the code:

1. First add the required data source configuration

# Data source, Druid is configured by default
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driverClassName=com.mysql.jdbc.Driver

#Multiple data source configuration
test.datasource.url=jdbc:mysql://192.168.1.2:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true
test.datasource.username=root
test.datasource.password=123456

#Database 2
test2.datasource.url=jdbc:mysql://192.168.1.3:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true
test2.datasource.username=root
test2.datasource.password=123456

2. Add a database enumeration

/**
 * List all data source keys (usually named by database name), which can be changed to your own data source name
 * Notice:
 * 1) Here the data source and database are one-to-one
 *2) The variable name in DatabaseType is the name of the database
 */
public enum DatabaseType {
    TEST_DB("TEST_DB"), TEST_DB2("TEST_DB2");

    private String code;

    private DatabaseType(String code)
    {
        this.code = code;
    }

    public String getCode()
    {
        return code;
    }

    public void setCode(String code)
    {
        this.code = code;
    }
}

3. Add the database identification class, no need to change it, just use it directly

/**
 * @Title: DataSourceContextHolder
 * @Description: A key step, an identification, when setting up the dynamic data source, two libraries are connected, but how to determine that each connection is the database that needs to be connected, then this identification is required.
 *
 * @author gogym
 * @date 12:21:31 PM
 */
public class DataSourceContextHolder
{
    private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<DatabaseType>();

    public static void setDbType(DatabaseType dbType)
    {
        contextHolder.set(dbType);
    }

    public static DatabaseType getDbType()
    {
        return contextHolder.get();
    }

    public static void clearDbType()
    {
        contextHolder.remove();
    }
}

4. Add dynamic data source. No need to change, just use

/**
 *
 * Dynamic data source
 * @author gogym
 * @version April 27, 2018
 * @see DynamicDataSource
 * @since
 */
public class DynamicDataSource extends AbstractRoutingDataSource
{
    @Override
    protected Object determineCurrentLookupKey()
    {
        return DataSourceContextHolder.getDbType();
    }
}

5. Add a custom annotation to specify the data source, you can directly apply it without changing it

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

/**
 *
 * Custom annotation to specify the data source
 * @author gogym
 * @version April 27, 2018
 * @see DS
 * @since
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DS {
    DatabaseType value();
}

6. Add an AOP to parse annotations. Apply directly without changing

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

/**
 *
 * Custom AOP aspect for parsing annotations
 * @author gogym
 * @version April 27, 2018
 * @see DynamicDataSourceAspect
 * @since
 */
@Aspect
@Component
public class DynamicDataSourceAspect
{
    @Before("@annotation(DS)")
    public void beforeSwitchDS(JoinPoint point)
    {

        // Get the currently accessed class
        Class<?> className = point.getTarget().getClass();

        // get access method name
        String methodName = point.getSignature().getName();
        // get the type of the method's parameter
        Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
        DatabaseType dataSource = DataSourceContextHolder.getDbType();
        try
        {
            // get access method object
            Method method = className.getMethod(methodName, argClass);

            // Determine if there is an @DS annotation
            if (method.isAnnotationPresent(DS.class))
            {
                DS annotation = method.getAnnotation(DS.class);
                // Get the data source name in the annotation
                dataSource = annotation.value();
            }
        }
        catch (Exception e)
        {
            e.printStackTrace ();
        }

        // switch data source
        DataSourceContextHolder.setDbType(dataSource);

    }

    @After("@annotation(DS)")
    public void afterSwitchDS(JoinPoint point)
    {

        DataSourceContextHolder.clearDbType();

    }
}

7. Add a database DAO, and the method of operating the database is basically satisfied. no need for correction

/**
 * Database operation DAO This is written to write less mapper mapping classes when using myBatis
 *
 * @author gogym
 * @version August 30, 2017
 * @see CommonDao
 * @since
 */
public interface CommonDao
{

    /**
     * save object
     *
     * @param str
     * @param obj
     * @return
     * @throws Exception
     */
    public Object save(String str, Object obj)
        throws Exception;

    /**
     * Modify the object
     *
     * @param str
     * @param obj
     * @return
     * @throws Exception
     */
    public Object update(String str, Object obj)
        throws Exception;

    /**
     * delete object
     *
     * @param str
     * @param obj
     * @return
     * @throws Exception
     */
    public Object delete(String str, Object obj)
        throws Exception;

    /**
     * find object
     *
     * @param str
     * @param obj
     * @return
     * @throws Exception
     */
    public Object findForObject(String str, Object obj)
        throws Exception;

    /**
     * Find object and return List
     *
     * @param str
     * @param obj
     * @return
     * @throws Exception
     */
    public Object findForList(String str, Object obj)
        throws Exception;

    /**
     * The search object is encapsulated into a Map
     *
     * @param s
     * @param obj
     * @return
     * @throws Exception
     */
    public Object findForMap(String sql, Object obj, String key, String value)
        throws Exception;

}

8. Add DAO implementation class. no need for correction

import org.mybatis.spring.support.SqlSessionDaoSupport;

public class CommonDaoImpl extends SqlSessionDaoSupport implements CommonDao
{

    @Override
    public Object save(String str, Object obj)
        throws Exception
    {

        return getSqlSession().insert(str, obj);
    }

    @Override
    public Object update(String str, Object obj)
        throws Exception
    {
        return getSqlSession().update(str, obj);
    }

    @Override
    public Object delete(String str, Object obj)
        throws Exception
    {
        return getSqlSession().delete(str, obj);
    }

    @Override
    public Object findForObject(String str, Object obj)
        throws Exception
    {
        return getSqlSession().selectOne(str, obj);
    }

    @Override
    public Object findForList(String str, Object obj)
        throws Exception
    {
        return getSqlSession().selectList(str, obj);
    }

    @Override
    public Object findForMap(String sql, Object obj, String key, String value)
        throws Exception
    {
        return getSqlSession().selectMap(sql, key, value);
    }

}

9. Add project configuration and modify it according to your own needs

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.poly.rbl.dao.CommonDao;
import com.poly.rbl.dao.impl.CommonDaoImpl;
import com.poly.rbl.datasource.DatabaseType;
import com.poly.rbl.datasource.DynamicDataSource;


@Configuration
public class ApplicationConfig extends WebMvcConfigurerAdapter
{

    // mapper file path, the path where you store the mapper file yourself
    static final String MAPPER_LOCATION = "classpath:mapper/**/*.xml";

    // mybatis configuration file path
    static final String MYBATIS_CONFIG = "xml/myBatisSetting.xml";

    @Autowired
    private Environment env;

    /**
     * Create 2 data sources (the name of the data source: the method name can be taken as XXXDataSource(), XXX is the database name, which is also the name of the data source)
     */

    private DataSource testDBdataSource()
        throws Exception
    {

        Properties props = new Properties();
        props.put("driverClassName", env.getProperty("spring.datasource.driverClassName"));
        props.put("url", env.getProperty("test.datasource.url"));
        props.put("username", env.getProperty("test.datasource.username"));
        props.put("password", env.getProperty("test.datasource.password"));
        return DruidDataSourceFactory.createDataSource(props);
    }

    private DataSource testDBdataSource2()
        throws Exception
    {

        Properties props = new Properties();
        props.put("driverClassName", env.getProperty("spring.datasource.driverClassName"));
        props.put("url", env.getProperty("test2.datasource.url"));
        props.put("username", env.getProperty("test2.datasource.username"));
        props.put("password", env.getProperty("test2.datasource.password"));
        return DruidDataSourceFactory.createDataSource(props);
    }

    /**
     * @throws Exception
     * @Primary This annotation indicates that when there are multiple implementation classes for the same interface that can be injected, which one is selected by default, instead of letting the @autowire annotation report an error
     * @Qualifier is injected by name, usually in one injection with multiple instances of the same type (e.g. with multiple instances of the DataSource type)
     */
    @Bean("dynamicDataSource")
    @Primary
    public DynamicDataSource dynamicDataSource()
        throws Exception
    {

        DataSource testDBdataSource = testDBdataSource();
        DataSource testDBdataSource2 = testDBdataSource2();

        // Multiple data sources can be added, if needed
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DatabaseType.TEST_DB, testDBdataSource);
        targetDataSources.put(DatabaseType.TEST_DB2, testDBdataSource2);

        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(targetDataSources);// This method is the method of AbstractRoutingDataSource
        dynamicDataSource.setDefaultTargetDataSource(testDBdataSource);// The default datasource is set to myTestDbDataSource
        return dynamicDataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DynamicDataSource dynamicDataSource)
        throws Exception
    {

        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();

        /** Set mybatis configuration scan path */
        sessionFactory.setConfigLocation(new ClassPathResource(MYBATIS_CONFIG));
        /** set datasource */
        sessionFactory.setDataSource(dynamicDataSource);
        /** Add mapper scan path */
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(ApplicationConfig.MAPPER_LOCATION));

        /** Set the typeAlias ​​package scan path */
        // sessionFactory.setTypeAliasesPackage("com....");

        return sessionFactory.getObject();
    }

    /**
     * Configure transaction manager
     */
    @Bean
    public DataSourceTransactionManager transactionManager(DynamicDataSource dynamicDataSource)
        throws Exception
    {
        return new DataSourceTransactionManager(dynamicDataSource);
    }

    /**
     * Inject a dao here. The purpose is to not want to write too many mapper classes, and only need one dao to execute all mybatis sql
     *
     * @param sqlSessionFactory
     * @return
     * @throws Exception
     */
    @Bean("commonDao")
    // @Lazy// Note that lazy loading annotations may be added here, otherwise spring will not know which one to load first, which is likely to cause circular dependencies
    public CommonDao commonDao(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory)
        throws Exception
    {
        CommonDaoImpl cd = new CommonDaoImpl();
        cd.setSqlSessionFactory(sqlSessionFactory);
        return cd;
    }

}

At this point, the basic configuration has been completed. It is very simple to use, just add an annotation directly to the service method

@Service
public class OrderDeliveryServiceImpl implements OrderDeliveryService
{

    @Autowired
    private CommonDao commonDao;

    @Override
    @DS(DatabaseType.TEST_DB2) //Specify to use test2 data source, if not specified, use test by default
    public OrderDelivery findByOrderNo(String orderNo)
    {

        try
        {
            return (OrderDelivery)commonDao.findForObject(
                MyBatisUtils.getSqlKey(OrderDelivery.class, "selectByOrderNo"), orderNo);
        }
        catch (Exception e)
        {
            e.printStackTrace ();
        }
        return null;
    }

}
/**
 * Map mapper file
 * @author gogym
 * @version 1.0
 */
public class MyBatisUtils
{

    private static String getPackageName(Class<?> clzss)
    {
        return clzss.getPackage().getName();
    }

    public static String getSqlKey(Class<?> clzss, String shortKey)
    {
        return getPackageName(clzss).concat(".").concat(clzss.getSimpleName()).concat(".").concat(
            shortKey);
    }
}

over.




Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325449029&siteId=291194637
Recommended