Java Learning, From Starting to Abandoning Spring MVC+Maven+Mybits Multi-database Configuration (mysql+sqlserver) AOP

Keywords: PHP Spring xml Database JDBC

There are two kinds of requirements for multi-database configuration: one is that the project is too big and the amount of visits is too high, so many applications have to distribute multiple databases to ease the pressure of accessing. The other is that two different database services should be integrated together now, even if the database is different, one is mysql. A SQL server, Xiaobian's current project belongs to the latter.

To achieve the separation of reading and writing, first of all, we must ensure that master-slave replication, that is, the data of the write database can be replicated to the read database in real time, so as to ensure that there is no difference between the data. This is not what we want to learn today. The small edition project is not used at present. This article is about multi-database configuration. There are two ways to integrate multiple databases: subcontracting and AOP. This paper only records AOP.

 

Refer to this article: https://www.cnblogs.com/weixupeng/p/9720472.html (typesetting is not very friendly)

1. Because of the use of Maven, first pom.xml introduces the driver jar package dependency to link the database, mysql can be introduced directly, but SQL server and orcale need to download to the local first, and then manually introduce before adding dependencies, such as in this SQL server example article: https://www.cnblogs.com/dawnheaven/p/5738477.html You can also download the latest version from other sources, but remember to write the correct version number when mvn.

mvn install:install-file -Dfile=sqljdbc4.jar -Dpackaging=jar -DgroupId=com.microsoft.sqlserver -DartifactId=sqljdbc4 -Dversion=4.0

Remember to change to your own version - Dfile= "absolute path of jar package + full file name version"
This approach is also applicable to Linux environments.

2. Configure the properties file. The name of this file may be different for everyone. Some people create new db.properties and some config.properties. This is irrelevant. Configure their own files in spring.xml file.

3. Configure spring.xml. This file also has many different names. Some articles use application-content.xml, some files are called spring mvc.xml, my name is spring-context.xml. This depends on which one I use for my project. Anyway, there are the following quotations

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation=" 
          http://www.springframework.org/schema/beans 
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
          http://www.springframework.org/schema/context 
          http://www.springframework.org/schema/context/spring-context-3.0.xsd
          http://www.springframework.org/schema/mvc     
          http://www.springframework.org/schema/mvc/spring-mvc.xsd
          http://www.springframework.org/schema/tx
          http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
          http://www.springframework.org/schema/aop 
          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

Some say the last line is http://Www.spring framework.org/schema/aop/spring-aop-3.0.xsd is sometimes said to be http://www.spring framework.org/schema/aop/spring-aop.xsd

Dependencies need to be added to pom.xml:

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.0</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.0</version>
        </dependency>

Other configurations are almost the same according to other articles, only one point needs attention!

    <bean id="dynamicDataSource" class="com.dataSourcer.ThreadLocalRountingDataSource">
        <property name="defaultTargetDataSource" ref="dataSource_daka"/>
        <property name="targetDataSources">
            <map key-type="com.dataSourcer.DataSources"> This place needs to use enumeration classes if the enumeration is defined by Java code, otherwise java.lang.String can be used.
                <entry key= "daka" value-ref= "dataSource_daka"> </entry> This should be consistent with the key value of the custom enumeration class.
                <entry key="kaoqin" value-ref="dataSource_kaoqin"></entry> 
            </map>
        </property>
    </bean>

4. Define your own database enumeration class.

5. Defining the ThreadLocalRountingDataSource class inherits from AbstractRoutingDataSource, which is basically the same as nothing special.

6. Customize annotation classes, which is very important but has nothing to say.

7. Define the database management class DataSourceType Manager (some named after DataSourceContextHolder, which is better understood by the Manager personally). ThreadLocal in this class still needs to be added to achieve thread safety, and the following is especially recommended:

    // ThreadLocal class is the key to thread security because most data operations are executed concurrently, so thread security must be considered.
    private static final ThreadLocal<DataSources> dataSourceTypes = new ThreadLocal<DataSources>() {
 
        @Override
        protected DataSources initialValue() {
            return DataSources.daka;
        }
    };

8. The final highlight is DynamicDataSourceAspect, which defines the cut-point, and of course spring.xml.

Finally, the following is a summary of the code for my project:

jdbc.url = jdbc:mysql://IP:3306 / database name? useUnicode=true&amp;characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
jdbc.username = Account number
jdbc.password= Password

sql.url = jdbc:sqlserver://ip:1433;databaseName = database name
sql.username = Account number
sql.password= Password
config.properties
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.0</version>
        </dependency>
        <dependency>
                <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.0</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.40</version>
        </dependency>
        <dependency>
                <groupId>com.microsoft.sqlserver</groupId>
                  <artifactId>sqljdbc4</artifactId>
                  <version>4.0</version>
          </dependency>sqlserver Remember to download and add dependencies manually        
Adding supplementary dependencies to pom.xml
<!-- Some are called spring-context.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:cache="http://www.springframework.org/schema/cache"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation=" 
          http://www.springframework.org/schema/cache    
            http://www.springframework.org/schema/cache/spring-cache-4.2.xsd
          http://www.springframework.org/schema/beans 
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
          http://www.springframework.org/schema/context 
          http://www.springframework.org/schema/context/spring-context-3.0.xsd
          http://www.springframework.org/schema/mvc     
          http://www.springframework.org/schema/mvc/spring-mvc.xsd
          http://www.springframework.org/schema/tx
          http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
          http://www.springframework.org/schema/aop 
          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <!-- Cache configuration(two types) -->
    <!-- Enabling Cache Annotation(Please configure it in Spring Main Configuration File) -->
    <cache:annotation-driven cache-manager="cacheManager" />
    <!-- Spring Provided based on Ehcache Implemented cache manager -->
    <bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="classpath:ehcache.xml" />
    </bean>
    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
        <property name="cacheManager" ref="cacheManagerFactory" />
    </bean>

    <!-- Marked@Controller Annotated classes are converted to bean -->
    <context:component-scan base-package="com.test.controller,com.test.service" /> 
    
    <!-- Multimedia parser -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="100000000"/>
        <property name="defaultEncoding" value="UTF-8"/>
    </bean>
        
    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/html;charset=UTF-8</value>
                        <value>application/json;charset=UTF-8</value>
                    </list>
                </property>
                <property name="features">
                    <array>
                        <value>WriteMapNullValue</value>
                        <value>WriteNullStringAsEmpty</value>
                    </array>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    
    <!-- Configure the data source. Here's your properties file -->
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
        <property name="location">  
            <value>/WEB-INF/classes/config.properties</value>  
        </property>  
        <property name="fileEncoding" value="utf-8" />  
    </bean>
    
    <bean id="dataSource_daka" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!-- Basic attributes url,user,password -->
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <!-- Configuration initialization size, minimum, maximum -->
        <property name="initialSize" value="1" />
        <property name="minIdle" value="1" />
        <property name="maxActive" value="20" />
        <!-- Configuration to get the connection waiting timeout time -->
        <property name="maxWait" value="60000" />
        <!-- How often is the configuration interval detected to detect idle connections that need to be closed in milliseconds? -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <!-- Configure the minimum lifetime of a connection in the pool in milliseconds -->
        <property name="minEvictableIdleTimeMillis" value="300000" />

        <property name="validationQuery" value="SELECT 'x'" />
        <property name="testWhileIdle" value="true" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />

        <!-- open PSCache,And specify on each connection PSCache Size -->
        <property name="poolPreparedStatements" value="false" />
        <property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
    </bean>
    <!-- Test Multiple Data Source 1 -->
    <bean id="dataSource_kaoqin" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!-- Basic attributes url,user,password -->
        <property name="url" value="${sql.url}" />  
        <property name="username" value="${sql.username}" />  
        <property name="password" value="${sql.password}" /> 
        <property name="connectionProperties" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"></property>
  
        <!-- Configuration initialization size, minimum, maximum -->
        <property name="initialSize" value="1" />
        <property name="minIdle" value="1" />
        <property name="maxActive" value="20" />
        <!-- Configuration to get the connection waiting timeout time -->
        <property name="maxWait" value="60000" />
        <!-- How often is the configuration interval detected to detect idle connections that need to be closed in milliseconds? -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <!-- Configure the minimum lifetime of a connection in the pool in milliseconds -->
        <property name="minEvictableIdleTimeMillis" value="300000" />

        <property name="validationQuery" value="SELECT 'x'" />
        <property name="testWhileIdle" value="true" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />

        <!-- open PSCache,And specify on each connection PSCache Size -->
        <property name="poolPreparedStatements" value="false" />
        <property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
    </bean> 
    
    <bean id="dynamicDataSource" class="com.dataSourcer.ThreadLocalRountingDataSource">
        <property name="defaultTargetDataSource" ref="dataSource_daka"/>
        <property name="targetDataSources">
            <map key-type="com.dataSourcer.DataSources">
                <entry key="daka" value-ref="dataSource_daka"></entry>
                <entry key="kaoqin" value-ref="dataSource_kaoqin"></entry> 
            </map>
        </property>
    </bean>
    <!-- open AOP -->
    <aop:aspectj-autoproxy expose-proxy="true" proxy-target-class="true" />
    <!-- Registration facets Bean -->
    <bean id="dynamicDataSourceAspect" class="com.dataSourcer.DynamicDataSourceAspect"></bean>

    
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dynamicDataSource" />
        <!-- <property name="configLocation" value="classpath:mybatis-config.xml"></property> -->
        <property name="mapperLocations">
            <list>
                <value>classpath:com/test/mapper/*.xml</value>
            </list>
        </property>
    </bean>
    
    <!-- Automatic scanning of all Mapper Interfaces and files -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.test.mapper"></property>
    </bean>
    
    <!-- affair -->
    <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dynamicDataSource"></property>
    </bean>
    
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
    
    
    

</beans>
Spr.xml configuration
package com.dataSourcer;

/**
 * Write an enumeration class to represent the key of the data source
 *
 */
public enum DataSources {
    daka,
    kaoqin
}
Enumeration class DataSources
package com.dataSourcer;
/**
 * Write thread-safe data source switching class DataSourceTypeManager 
 *
 */
public class DataSourceTypeManager {
    // ThreadLocal Class is the key to thread security, because most data operations are executed concurrently, so thread security must be considered.
    private static final ThreadLocal<DataSources> dataSourceTypes = new ThreadLocal<DataSources>() {
 
        @Override
        protected DataSources initialValue() {
            return DataSources.daka;
        }
    };
 
    public static DataSources get() {
        return dataSourceTypes.get();
    }
 
    public static void set(DataSources dataSourceType) {
        dataSourceTypes.set(dataSourceType);
    }
 
    public static void reset() {
        dataSourceTypes.set(DataSources.daka);
    }
    
    public static void clear() {
        dataSourceTypes.remove();
    }

}
DataSourceTypeManager
package com.dataSourcer;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
 * Extended class AbstractRoutingDataSource 
 *
 */
public class ThreadLocalRountingDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceTypeManager.get();
    }

}
ThreadLocalRountingDataSource
package com.dataSourcer;

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

/**
 * Writing custom annotation classes 
 *
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetDataSource {

    DataSources dataSourceKey() default DataSources.daka;
}
Writing a custom annotation class TargetDataSource
package com.dataSourcer;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

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.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

/**
 * Write the Data Source Switching Aspect Class: DynamicDataSourceAspect 
 *
 */
@Aspect
@Order(-1)
@Component
public class DynamicDataSourceAspect {
    
    @Pointcut("execution(* com.test.service.*.*(..))")
    public void pointCut() {
        
    }
 
    /**
     * Replacement of data sources before execution of methods
     *
     * @param joinPoint        Tangent point
     * @param targetDataSource Dynamic Data Source
     */
    @Before("@annotation(targetDataSource)")
    public void doBefore(JoinPoint joinPoint, TargetDataSource targetDataSource) {
        DataSources dataSourceKey = targetDataSource.dataSourceKey();
        if (dataSourceKey == DataSources.kaoqin) {
            DataSourceTypeManager.set(DataSources.kaoqin);
        } else {
            DataSourceTypeManager.set(DataSources.daka);
        }
    }
 
    /**
     * Clear the data source settings after executing the method
     *
     * @param joinPoint        Tangent point
     * @param targetDataSource Dynamic Data Source
     */
    @After("@annotation(targetDataSource)")
    public void doAfter(JoinPoint joinPoint, TargetDataSource targetDataSource) {
        DataSourceTypeManager.clear();;
    }
 
    @Before(value = "pointCut()")
    public void doBeforeWithSlave(JoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        //Getting Current Tangent Method Objects
        Method method = methodSignature.getMethod();
        if (method.getDeclaringClass().isInterface()) {//Judging whether it is an interface method
            try {
                //Method Objects for Getting Actual Types
                method = joinPoint.getTarget().getClass()
                        .getDeclaredMethod(joinPoint.getSignature().getName(), method.getParameterTypes());
            } catch (NoSuchMethodException e) {
            }
        }
        if (null == method.getAnnotation(TargetDataSource.class)) {
            DataSourceTypeManager.set(DataSources.daka);
        }
    }
}
Data Source Switching Aspect Class: DynamicDataSourceAspect

Finally, in Service, use the following ways:

package com.test.service;

import java.util.HashMap;
import java.util.List;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.dataSourcer.DataSources;
import com.dataSourcer.TargetDataSource;
import com.test.mapper.KaoqinSqlserverMapper; 

@Service
public class KaoqinSqlserverService {
@Autowired
private KaoqinSqlserverMapper kaoqin; /** Summary of staff attendance in the last 10 months */ @TargetDataSource(dataSourceKey = DataSources.kaoqin) public List<HashMap<String, Object>> userkaoqin_all(String emplid){ return kaoqin.userkaoqin_all(emplid); } /** Details of employee's monthly attendance */ @TargetDataSource(dataSourceKey = DataSources.kaoqin) public List<HashMap<String, Object>> userkaoqin_details(String emplid, String minday, String maxday){ return kaoqin.userkaoqin_details(emplid, minday, maxday); } }

 

The following article is called in Controller. I haven't tested it. If you are interested, you can see: https://www.cnblogs.com/haha12/p/10613549.html

Posted by darkfunkel on Mon, 05 Aug 2019 21:49:34 -0700