maven/mybatis-plus/spring动态数据源/springmvc/AOP/自定义参数注入/druid/dom4j/generator代码生成器

来源:互联网 发布:函授和网络教育区别 编辑:程序博客网 时间:2024/06/16 16:33

mybatis-plus+动态数据源+自定义数据源配置+dom解析+自定义参数注入(注解)+AOP切换数据源+druid

项目需求引入租户概念,每个租户一个域名,由于每个租户的用户量比较大,为方便数据管理和减轻数据库压力,要求使用多数据源,根据租户域名路由到对应租户的数据库。在网上看了好多关于多数据源的文章,感觉都不合适。几乎所有的文章都需要在配置文件中配置多个数据源,如果一个租户即有app,又有web,那相同的租户就要配置2个相同的数据源,配置文件会变的很繁琐。于是想把数据源配置文件独立出来,在项目加载数据源的时候自动解析并注入多数据源。

感谢同事架构师萌琪琪里面一些实现参考了他的多数据源,他用的是AbstractDataSource实现的多数据源路由,不用默认数据源什么的,更接近底层一些。

参考博客:http://icezx.iteye.com/blog/1939586

然后自己整理出一个简单的项目实例,由于还没有实际应用,难免有疏漏或写的不好的地方,欢迎指正。

看下项目结构:


具体的过程我就不多解释了,请参考博客:http://icezx.iteye.com/blog/1939586

项目实例下载地址:点这里!(http://download.csdn.net/download/kaer_gg/10000306)

简单介绍下实现逻辑和代码吧:

项目启动时通过DynamicDataSource加载数据源,此过程涉及解析自定义的数据库配置文件dataSource.xml。然后controller通过截获请求的url获取用户访问的域名,并通过注解或指定自定义类型对象自动注入到controller参数中,然后通过service切面取得参数做为数据库切换依据进行数据库切换,实现根据不同租户的域名访问项目进行对应租户数据库的访问。

自定义注解:CurrentTenantKey.java

package com.guorucheng.common.annotation;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.PARAMETER)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface CurrentTenantKey {}
controller使用自定义注解或指定参数对象注入:CurrentTenantKeyResolver.java
package com.guorucheng.common.annotation.resolver;import javax.servlet.http.HttpServletRequest;import org.springframework.core.MethodParameter;import org.springframework.web.bind.WebDataBinder;import org.springframework.web.bind.support.WebDataBinderFactory;import org.springframework.web.context.request.NativeWebRequest;import org.springframework.web.method.support.HandlerMethodArgumentResolver;import org.springframework.web.method.support.ModelAndViewContainer;import com.guorucheng.common.annotation.CurrentTenantKey;import com.guorucheng.common.entity.TenantKey;import com.guorucheng.common.utils.StringUtil;/** * CurrentTenantKey 注解解析器 * @author GR·cheng * */public class CurrentTenantKeyResolver implements HandlerMethodArgumentResolver {//private final Pattern PATTERN = Pattern.compile("^http://((\\w+\\.){1,2}guorucheng\\.com)");/** *  * @Title: resolveArgument * @Description: 将request中的请求参数解析到当前controller参数上 * @param parameter 需要被解析的controller参数,此参数必须先传给{@link #supportsParameter}并返回true * @param mavContainer 当前request的ModelAndViewContainer * @param request 当前request * @param binderFactory 生成{@link WebDataBinder}实例的工厂 * @return 解析后的controller参数 * @throws Exception  * @see org.springframework.web.method.support.HandlerMethodArgumentResolver#resolveArgument(org.springframework.core.MethodParameter, org.springframework.web.method.support.ModelAndViewContainer, org.springframework.web.context.request.NativeWebRequest, org.springframework.web.bind.support.WebDataBinderFactory) * @user GR·cheng * @date 2017年9月6日 */@Overridepublic Object resolveArgument(MethodParameter parameter,ModelAndViewContainer mavContainer, NativeWebRequest request,WebDataBinderFactory binderFactory) throws Exception {TenantKey tenantKey = new TenantKey();String url = request.getNativeRequest(HttpServletRequest.class).getServerName().toString();if (StringUtil.notEmpty(url)) {tenantKey.setTenantKey(url);} else {tenantKey.setTenantKey("www.guorucheng.com");}return tenantKey;}/** *  * @Title: supportsParameter * @Description: 指定参数如果被应用@CurrentTenantKey,则使用该注解。 * @param parameter 当前使用注解的参数 * @return 如果支持当前使用注解的参数,返回true,不支持返回false。如果直接返回true,则表示支持所有参数。 * @see org.springframework.web.method.support.HandlerMethodArgumentResolver#supportsParameter(org.springframework.core.MethodParameter) * @user GR·cheng * @date 2017年9月6日 */@Overridepublic boolean supportsParameter(MethodParameter parameter) {return parameter.hasParameterAnnotation(CurrentTenantKey.class) || parameter.getParameterType() == TenantKey.class;}}
切面截获切换依据并设置当前线程数据源:TenantKeyAspect.java

package com.guorucheng.common.aspect;import java.lang.reflect.Method;import org.springframework.aop.AfterReturningAdvice;import org.springframework.aop.MethodBeforeAdvice;import com.guorucheng.common.entity.TenantKey;import com.guorucheng.common.holder.DataSourceHolder;public class TenantKeyAspect implements MethodBeforeAdvice,AfterReturningAdvice {/** *  * @Title: before 执行方法前切换数据源 * @Description:  * @param method 方法名 * @param parameters 参数组 * @param ASP_CLASS 调用的类对象 * @throws Throwable  * @see org.springframework.aop.MethodBeforeAdvice#before(java.lang.reflect.Method, java.lang.Object[], java.lang.Object) * @user GR·cheng * @date 2017年9月28日 */@Overridepublic void before(Method method, Object[] parameters, Object ASP_CLASS)throws Throwable {TenantKey tenantKey = null;for (Object object : parameters) {//入参查找租户if (object instanceof TenantKey) {tenantKey = (TenantKey) object;break;}}if (tenantKey == null) {//入参无租户,抛出异常throw new RuntimeException("service层切面未获取到tenantKey");}DataSourceHolder.setDataSourceType(tenantKey.getTenantKey());}/** *  * @Title: afterReturning 方法执行return前清除线程数据源 * @Description:  * @param returnObject 调用方法返回的对象 * @param method 方法名 * @param parameters 参数组 * @param ASP_CLASS 调用的类对象 * @throws Throwable  * @see org.springframework.aop.AfterReturningAdvice#afterReturning(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], java.lang.Object) * @user GR·cheng * @date 2017年9月28日 */@Overridepublic void afterReturning(Object returnObject, Method method, Object[] parameters, Object ASP_CLASS) throws Throwable {DataSourceHolder.removeDataSourceType();}}
多数据源创建、管理、切换类:DynamicDataSource.java

package com.guorucheng.common.dynamicDataSource;import java.sql.SQLException;import java.util.List;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;import javax.sql.DataSource;import org.apache.log4j.Logger;import org.springframework.beans.BeansException;import org.springframework.beans.factory.support.DefaultListableBeanFactory;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;import com.alibaba.druid.pool.DruidDataSource;import com.guorucheng.common.entity.DataSourceConfig;import com.guorucheng.common.entity.TenantKey;import com.guorucheng.common.holder.DataSourceHolder;import com.guorucheng.common.utils.StringUtil;import com.guorucheng.common.xmlConf.XmlConfInitStrategy;/** * 初始化动态数据源,并根据上下文变量切换数据源 * @author GR·cheng * */public class DynamicDataSource extends AbstractRoutingDataSource implements ApplicationContextAware {private static Logger log = Logger.getLogger(DynamicDataSource.class);//数据源配置文件名称private String configName = "";//bean工厂private static ApplicationContext context;//数据源配置列表private List<DataSourceConfig> configs = null;//数据源池public final ConcurrentHashMap<String, DataSource> dataSourcePool = new ConcurrentHashMap<String, DataSource>();//初始化多数据源@Overridepublic void afterPropertiesSet() {try {initDataSources();} catch (Exception e) {e.printStackTrace();}super.afterPropertiesSet();}//获取bean工厂@Overridepublic void setApplicationContext(ApplicationContext applicationContext)throws BeansException {context = applicationContext;}/** *  *  * @Title: initDataSources * @Description: 初始化数据源 * @param:  * @return: void * @user: GR·cheng * */private void initDataSources() {configs = XmlConfInitStrategy.init().getInitConfig(configName);DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getAutowireCapableBeanFactory();for (DataSourceConfig config : configs) {if (!config.checkConfig()) {throw new RuntimeException("数据库配置文件缺少关键字段值");}DataSource dataSource = createDataSource(config);List<TenantKey> tenantKeys = config.buildTenantKeyList();for (TenantKey tenantKey : tenantKeys) {if (dataSourcePool.containsKey(tenantKey)) {log.info("数据库连接池中已存在数据源:" + tenantKey.getTenantKey());} else {beanFactory.registerSingleton(tenantKey.getTenantKey(), dataSource);dataSourcePool.put(tenantKey.getTenantKey(), dataSource);}}}this.setTargetDataSources(dataSourcePool);setDefaultTargetDataSource(dataSourcePool.get("www.guorucheng.com"));}/** *  *  * @Title: createDataSource * @Description: 创建数据源 * @param: @param config * @param: @return * @return: DataSource * @user: GR·cheng * */private DataSource createDataSource(DataSourceConfig config) {if (config == null) {return null;}DruidDataSource dataSource = new DruidDataSource();dataSource.setUrl(config.getUrl());dataSource.setUsername(config.getUsername());dataSource.setPassword(config.getPassword());String initialSizeStr = config.getInitialSize();if (StringUtil.notEmpty(initialSizeStr)) {try {Integer initialSize = Integer.parseInt(initialSizeStr);dataSource.setInitialSize(initialSize);} catch (NumberFormatException e) {throw new RuntimeException("xml datasource config initialSize invalid");}}String maxActiveStr = config.getMaxActive();if (StringUtil.notEmpty(maxActiveStr)) {try {Integer maxActive = Integer.parseInt(maxActiveStr);dataSource.setMaxActive(maxActive);} catch (NumberFormatException e) {throw new RuntimeException("xml datasource config maxActive invalid");}}String minIdleStr = config.getMinIdle();if (StringUtil.notEmpty(minIdleStr)) {try {Integer minIdle = Integer.parseInt(minIdleStr);dataSource.setMinIdle(minIdle);} catch (NumberFormatException e) {throw new RuntimeException("xml datasource config minIdle invalid");}}String maxWaitStr = config.getMaxWait();if (StringUtil.notEmpty(maxWaitStr)) {try {Long maxWait = Long.parseLong(maxWaitStr);dataSource.setMaxWait(maxWait);} catch (NumberFormatException e) {throw new RuntimeException("xml datasource config maxWait invalid");}}String validationQuery = config.getValidationQuery();if (StringUtil.notEmpty(validationQuery)) {dataSource.setValidationQuery(validationQuery);}String testOnBorrowStr = config.getTestOnBorrow();if (StringUtil.notEmpty(testOnBorrowStr)) {Boolean testOnBorrow = Boolean.parseBoolean(testOnBorrowStr);dataSource.setTestOnBorrow(testOnBorrow);}String testOnReturnStr = config.getTestOnReturn();if (StringUtil.notEmpty(testOnReturnStr)) {Boolean testOnReturn = Boolean.parseBoolean(testOnReturnStr);dataSource.setTestOnReturn(testOnReturn);}String testWhileIdleStr = config.getTestWhileIdle();if (StringUtil.notEmpty(testWhileIdleStr)) {Boolean testWhileIdle = Boolean.parseBoolean(testWhileIdleStr);dataSource.setTestWhileIdle(testWhileIdle);}String timeBetweenEvictionRunsMillisStr = config.getTimeBetweenEvictionRunsMillis();if (StringUtil.notEmpty(timeBetweenEvictionRunsMillisStr)) {try {Long timeBetweenEvictionRunsMillis = Long.parseLong(timeBetweenEvictionRunsMillisStr);dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);} catch (NumberFormatException e) {throw new RuntimeException("xml datasource config timeBetweenEvictionRunsMillis invalid");}}String minEvictableIdleTimeMillisStr = config.getMinEvictableIdleTimeMillis();if (StringUtil.notEmpty(minEvictableIdleTimeMillisStr)) {try {Long minEvictableIdleTimeMIllis = Long.parseLong(minEvictableIdleTimeMillisStr);dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMIllis);} catch (NumberFormatException e) {throw new RuntimeException("xml datasource config minEvictableIdleTimeMIllis invalid");}}String removeAbandonedStr = config.getRemoveAbandoned();if (StringUtil.notEmpty(removeAbandonedStr)) {Boolean removeAbandoned = Boolean.parseBoolean(removeAbandonedStr);dataSource.setRemoveAbandoned(removeAbandoned);}String removeAbandonedTimeoutStr = config.getRemoveAbandonedTimeout();if (StringUtil.notEmpty(removeAbandonedTimeoutStr)) {Integer removeAbandonedTimeout = Integer.parseInt(removeAbandonedTimeoutStr);dataSource.setRemoveAbandonedTimeout(removeAbandonedTimeout);}String logAbandonedStr = config.getLogAbandoned();if (StringUtil.notEmpty(logAbandonedStr)) {Boolean logAbandoned = Boolean.parseBoolean(logAbandonedStr);dataSource.setLogAbandoned(logAbandoned);}try {dataSource.init();} catch (SQLException e) {throw new RuntimeException("drudDataSource init fail tenant is "+config.getTenantKeyList(),e);}return dataSource;}//查找当前用户上下文变量中设置的数据源@Overrideprotected Object determineCurrentLookupKey() {return DataSourceHolder.getDataSourceType();}//设置默认数据源@Overridepublic void setDefaultTargetDataSource(Object defaultTargetDataSource) {super.setDefaultTargetDataSource(defaultTargetDataSource);}//设置数据源集合@SuppressWarnings({ "rawtypes", "unchecked" })@Overridepublic void setTargetDataSources(Map targetDataSources){super.setTargetDataSources(targetDataSources);}public String getConfigName() {return configName;}public void setConfigName(String configName) {this.configName = configName;}}
druid连接池实体类:DataSourceConfig.java

package com.guorucheng.common.entity;import java.util.ArrayList;import java.util.List;import com.guorucheng.common.utils.StringUtil;public class DataSourceConfig {/** * 允许接入的租户域名列表 */private List<String> tenantKeyList;/** * 数据库地址 */private String url;/** * 数据库登录名 */private String username;/** * 数据库登录密码 */private String password;/** * 初始化连接池大小 */private String initialSize;/** * 连接池最大使用连接数 */private String maxActive;/** * 连接池最小空闲数 */private String minIdle;/** * 获取连接最大等待时间 */private String maxWait;/** * 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。 */private String validationQuery;/** * 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */private String testOnBorrow;/** * 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */private String testOnReturn;/** * 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。建议配置为true,不影响性能,并且保证安全性。 */private String testWhileIdle;/** * 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒。 */private String timeBetweenEvictionRunsMillis;/** * 一个连接在连接池中的最小生存时间,单位毫秒。 */private String minEvictableIdleTimeMillis;/** * 对于长时间不使用的连接是否强制关闭 */private String removeAbandoned;/** * 强制关闭长时间不适用的空闲连接的时间 */private String removeAbandonedTimeout;/** * 是否将关闭当前长时间未用的连接记录日志 */private String logAbandoned;public List<String> getTenantKeyList() {return tenantKeyList;}public void setTenantKeyList(List<String> tenantKeyList) {this.tenantKeyList = tenantKeyList;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getInitialSize() {return initialSize;}public void setInitialSize(String initialSize) {this.initialSize = initialSize;}public String getMaxActive() {return maxActive;}public void setMaxActive(String maxActive) {this.maxActive = maxActive;}public String getMinIdle() {return minIdle;}public void setMinIdle(String minIdle) {this.minIdle = minIdle;}public String getMaxWait() {return maxWait;}public void setMaxWait(String maxWait) {this.maxWait = maxWait;}public String getValidationQuery() {return validationQuery;}public void setValidationQuery(String validationQuery) {this.validationQuery = validationQuery;}public String getTestOnBorrow() {return testOnBorrow;}public void setTestOnBorrow(String testOnBorrow) {this.testOnBorrow = testOnBorrow;}public String getTestOnReturn() {return testOnReturn;}public void setTestOnReturn(String testOnReturn) {this.testOnReturn = testOnReturn;}public String getTestWhileIdle() {return testWhileIdle;}public void setTestWhileIdle(String testWhileIdle) {this.testWhileIdle = testWhileIdle;}public String getTimeBetweenEvictionRunsMillis() {return timeBetweenEvictionRunsMillis;}public void setTimeBetweenEvictionRunsMillis(String timeBetweenEvictionRunsMillis) {this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;}public String getMinEvictableIdleTimeMillis() {return minEvictableIdleTimeMillis;}public void setMinEvictableIdleTimeMillis(String minEvictableIdleTimeMillis) {this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;}public String getRemoveAbandoned() {return removeAbandoned;}public void setRemoveAbandoned(String removeAbandoned) {this.removeAbandoned = removeAbandoned;}public String getRemoveAbandonedTimeout() {return removeAbandonedTimeout;}public void setRemoveAbandonedTimeout(String removeAbandonedTimeout) {this.removeAbandonedTimeout = removeAbandonedTimeout;}public String getLogAbandoned() {return logAbandoned;}public void setLogAbandoned(String logAbandoned) {this.logAbandoned = logAbandoned;}public boolean checkConfig() {if (tenantKeyList == null || tenantKeyList.size() == 0 || StringUtil.isEmpty(url) || StringUtil.isEmpty(username) || StringUtil.isEmpty(password)) {return false;}return true;}public List<TenantKey> buildTenantKeyList() {List<TenantKey> keys = new ArrayList<TenantKey>();for (String tenantKey : tenantKeyList) {keys.add(new TenantKey(tenantKey));}return keys;}}
租户实体类:TenantKey.java

package com.guorucheng.common.entity;public class TenantKey {private String tenantKey;public TenantKey() {}public TenantKey(String tenantKey) {super();this.tenantKey = tenantKey;}public String getTenantKey() {return tenantKey;}public void setTenantKey(String tenantKey) {this.tenantKey = tenantKey;}@Overridepublic String toString() {return "TenantKey [tenantKey=" + tenantKey + "]";}}

代码生成器:MpGenerator.java

package com.guorucheng.common.generator;import com.baomidou.mybatisplus.generator.AutoGenerator;import com.baomidou.mybatisplus.generator.config.DataSourceConfig;import com.baomidou.mybatisplus.generator.config.GlobalConfig;import com.baomidou.mybatisplus.generator.config.PackageConfig;import com.baomidou.mybatisplus.generator.config.StrategyConfig;import com.baomidou.mybatisplus.generator.config.rules.DbType;import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;public class MpGenerator {public static void main(String[] args) {AutoGenerator ag = new AutoGenerator();//全局配置GlobalConfig gc = new GlobalConfig();gc.setOutputDir("D://test-Workspace//GuoRuCheng//src//main//java");gc.setFileOverride(true);// 是否覆盖文件gc.setActiveRecord(true);// 开启activeRecord 模式gc.setEnableCache(false);// XML 二级缓存gc.setBaseResultMap(true);// XML ResultMapgc.setBaseColumnList(true);// XML columListgc.setAuthor("GR·cheng");// 自定义文件命名,注意 %s 会自动填充表实体属性!// .setMapperName("%sDao")// .setXmlName("%sDao")// .setServiceName("MP%sService")// .setServiceImplName("%sServiceDiy")// .setControllerName("%sAction")ag.setGlobalConfig(gc);//数据源配置DataSourceConfig dsc = new DataSourceConfig();dsc.setDbType(DbType.MYSQL);dsc.setDriverName("com.mysql.jdbc.Driver");dsc.setUrl("jdbc:mysql://127.0.0.1:3306/db1?characterEncoding=utf8");dsc.setUsername("root");dsc.setPassword("root");ag.setDataSource(dsc);//生成策略StrategyConfig sc = new StrategyConfig();sc.containsTablePrefix("");sc.setNaming(NamingStrategy.underline_to_camel);//表名生成策略ag.setStrategy(sc);PackageConfig pc = new PackageConfig();pc.setParent("com.guorucheng");pc.setEntity("model");pc.setMapper("dao");pc.setXml("mapper");pc.setService("service");pc.setServiceImpl("service.serviceImpl");pc.setController("controller");ag.setPackageInfo(pc);ag.execute();}}

线程数据源管理器:DataSourceHolder.java

package com.guorucheng.common.holder;/** * 线程数据源管理器 * @author GR·cheng * */public class DataSourceHolder {private static final ThreadLocal<String> DATA_SOURCE_HOLDER = new ThreadLocal<String>();public static void setDataSourceType(String dataSourceType) {DATA_SOURCE_HOLDER.set(dataSourceType);}public static String getDataSourceType() {return DATA_SOURCE_HOLDER.get();}public static void removeDataSourceType() {DATA_SOURCE_HOLDER.remove();}}

自定义数据库配置文件解析器:XmlConfInitStrategy.java

package com.guorucheng.common.xmlConf;import java.io.InputStream;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.List;import org.apache.log4j.Logger;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.io.SAXReader;import com.guorucheng.common.entity.DataSourceConfig;import com.guorucheng.common.utils.StringUtil;/** * 自定义xml解析 * @author GR·cheng * */public class XmlConfInitStrategy {private final Logger LOG = Logger.getLogger(this.getClass());public static XmlConfInitStrategy xmlConfInitStrategy;private final String DEFAULT_CONF_FILE = "dataSource-conf.xml";public static XmlConfInitStrategy init() {return xmlConfInitStrategy = new XmlConfInitStrategy();}public List<DataSourceConfig> getInitConfig(String configName) {return parseConfigXml(StringUtil.isEmpty(configName)?loadConfig(DEFAULT_CONF_FILE):loadConfig(configName+".xml"));}@SuppressWarnings("unchecked")private List<DataSourceConfig> parseConfigXml(InputStream configXml){if (configXml == null) {return null;}Document document = null;SAXReader saxReader = new SAXReader();List<DataSourceConfig> dataSourceConfigs = new ArrayList<DataSourceConfig>();try {document = saxReader.read(configXml);Element root = document.getRootElement();List<Element> nodes = root.elements();for (Element node : nodes) {DataSourceConfig config = new DataSourceConfig();String tenantKeys = node.elementTextTrim("tenantKey");String[] tenantKeyArr = tenantKeys.split(";");List<String> tenantKeyList = new ArrayList<String>();for (String tenantKey : tenantKeyArr) {tenantKeyList.add(tenantKey);}config.setTenantKeyList(tenantKeyList);Element dataSource = node.element("dataSource");List<Element> dataSources = dataSource.elements();for (Element element : dataSources) {String propName = element.getName();try {Field field = config.getClass().getDeclaredField(propName);Method setter = config.getClass().getMethod("set"+propName.substring(0,1).toUpperCase()+propName.substring(1), field.getType());setter.invoke(config, element.getTextTrim());} catch (NoSuchFieldException e) {e.printStackTrace();} catch (SecurityException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}dataSourceConfigs.add(config);}} catch (DocumentException e) {e.printStackTrace();}return dataSourceConfigs;}/** * 加载src/main/resources中指定名称的配置文件 * @param configPath 文件名称 * @return 输入流 */private InputStream loadConfig(String configName){InputStream stream = this.getClass().getClassLoader().getResourceAsStream(configName);if (stream==null) {LOG.info("dataSource xml is not exist!");return null;}return stream;}}

项目中的应用:controller层

/** *  *  * @Title: findSysUsers * @Description: 使用自动识别注参并进行数据源切换查询 * @param: @param name * @param: @param tenantKey * @param: @return * @return: List<SysUser> * @user: GR·cheng * */@ResponseBody@RequestMapping("/findSysUsers")public List<SysUser> findSysUsers(TenantKey tenantKey) {return userService.findSysUsers(tenantKey);}/** *  *  * @Title: findSysUsers * @Description: 使用注解注参并进行数据源切换查询 * @param: @param name * @param: @param tenantKey * @param: @return * @return: List<SysUser> * @user: GR·cheng * */@ResponseBody@RequestMapping("/findSysUsers1")public List<SysUser> findSysUsers1(@CurrentTenantKey TenantKey tenantKey) {return userService.findSysUsers(tenantKey);}

service层实现时切换数据源,到这里就不用写代码了,切面类自动就切换数据源了,由于使用了mybatis-plus,使用框架自带的通用dao就好了,非常方便,不用自己再写代码了,dao和mapper都不用写。

package com.guorucheng.service.serviceImpl;import java.util.List;import com.guorucheng.model.SysUser;import com.guorucheng.common.entity.TenantKey;import com.guorucheng.dao.SysUserMapper;import com.guorucheng.service.ISysUserService;import com.baomidou.mybatisplus.service.impl.ServiceImpl;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;/** * <p> * 系统用户表 服务实现类 * </p> * * @author GR·cheng * @since 2017-08-31 */@Servicepublic class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {@Autowiredprivate SysUserMapper userDAO;@Overridepublic List<SysUser> findSysUsers(TenantKey tenantKey) {return userDAO.selectList(null);}}

dao:

package com.guorucheng.dao;import com.guorucheng.model.SysUser;import com.baomidou.mybatisplus.mapper.BaseMapper;/** * <p>  * 系统用户表 Mapper 接口 * </p> * * @author GR·cheng * @since 2017-08-31 */public interface SysUserMapper extends BaseMapper<SysUser> {}

mapper:

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.guorucheng.dao.SysUserMapper"><!-- 通用查询映射结果 --><resultMap id="BaseResultMap" type="com.guorucheng.model.SysUser"><id column="id" property="id" /><result column="name" property="name" /><result column="age" property="age" /><result column="ctime" property="ctime" /></resultMap>    <!-- 通用查询结果列 -->    <sql id="Base_Column_List">        id, name, age, ctime    </sql></mapper>

接下来是相关配置文件:

spring.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:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">    <!-- Service包(自动注入) -->    <context:component-scan base-package="com.guorucheng.service"/>    <import resource="classpath:spring-mybatis.xml"/></beans>

spring-mybaties.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: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.xsd       http://www.springframework.org/schema/tx       http://www.springframework.org/schema/tx/spring-tx.xsd       http://www.springframework.org/schema/aop       http://www.springframework.org/schema/aop/spring-aop.xsd">        <bean id="dynamicDataSource" class="com.guorucheng.common.dynamicDataSource.DynamicDataSource" >    <property name="configName" value="dataSource" /><!-- value值为数据库配置文件xml名称 -->    </bean>    <!-- Spring整合Mybatis,更多查看文档:http://mp.baomidou.com -->    <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">        <property name="dataSource" ref="dynamicDataSource"/>        <!-- 自动扫描Mapping.xml文件 -->        <property name="mapperLocations" value="classpath:com/guorucheng/mapper/*.xml"/>        <property name="configLocation" value="classpath:mybatis-config.xml"/>        <!-- <property name="typeAliasesPackage" value="com.guorucheng.model"/> -->        <property name="plugins">            <array>                <!-- 分页插件配置 -->                <bean id="paginationInterceptor" class="com.baomidou.mybatisplus.plugins.PaginationInterceptor">                </bean>            </array>        </property>    <!-- 全局配置注入 -->    <property name="globalConfig" ref="globalConfig" /></bean><bean id="globalConfig" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">    <!--AUTO->`0`("数据库ID自增") INPUT->`1`(用户输入ID")ID_WORKER->`2`("全局唯一ID")UUID->`3`("全局唯一ID")-->    <property name="idType" value="2" /><!--MYSQL->`mysql`ORACLE->`oracle`DB2->`db2`H2->`h2`HSQL->`hsql`SQLITE->`sqlite`POSTGRE->`postgresql`SQLSERVER2005->`sqlserver2005`SQLSERVER->`sqlserver`--><!-- Oracle需要添加该项 -->    <!-- <property name="dbType" value="oracle" /> -->    <!-- 全局表为下划线命名设置 true -->    <!-- <property name="dbColumnUnderline" value="true" /> -->        <!-- <property name="metaObjectHandler">            <bean class="com.ssm.common.MyMetaObjectHandler" />        </property> --></bean>    <!-- MyBatis 动态扫描  -->    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">        <property name="basePackage" value="com.guorucheng.dao"/>        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />    </bean>    <!-- 配置事务管理 -->    <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property name="dataSource" ref="dynamicDataSource"/>    </bean>    <!-- 事务管理 属性 -->    <tx:advice id="transactionAdvice" transaction-manager="transactionManager">        <tx:attributes>            <tx:method name="add*" propagation="REQUIRED"/>            <tx:method name="append*" propagation="REQUIRED"/>            <tx:method name="save*" propagation="REQUIRED"/>            <tx:method name="update*" propagation="REQUIRED"/>            <tx:method name="modify*" propagation="REQUIRED"/>            <tx:method name="edit*" propagation="REQUIRED"/>            <tx:method name="insert*" propagation="REQUIRED"/>            <tx:method name="delete*" propagation="REQUIRED"/>            <tx:method name="remove*" propagation="REQUIRED"/>            <tx:method name="repair" propagation="REQUIRED"/>            <tx:method name="get*" propagation="REQUIRED" read-only="true"/>            <tx:method name="find*" propagation="REQUIRED" read-only="true"/>            <tx:method name="load*" propagation="REQUIRED" read-only="true"/>            <tx:method name="search*" propagation="REQUIRED" read-only="true"/>            <tx:method name="datagrid*" propagation="REQUIRED" read-only="true"/>            <tx:method name="*" propagation="REQUIRED" read-only="true"/>        </tx:attributes>    </tx:advice>        <bean id="tenantKeyAspect" class="com.guorucheng.common.aspect.TenantKeyAspect" />    <!-- 配置事务切面和租户切面 -->    <aop:config>        <aop:pointcut id="transactionPointcut" expression="execution(* com.guorucheng.service..*.*(..))"/>        <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" order="2"/>        <aop:advisor pointcut-ref="transactionPointcut" advice-ref="tenantKeyAspect" order="1"/><!-- 数据源切换要在事务之前,否则会导致事务无效 -->    </aop:config>        </beans>

spring-mvc.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:context="http://www.springframework.org/schema/context"       xmlns:mvc="http://www.springframework.org/schema/mvc"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">    <mvc:default-servlet-handler/>    <!-- Controller包(自动注入) -->    <context:component-scan base-package="com.guorucheng.controller"/>    <mvc:annotation-driven>    <!-- 注册自定义参数解析器 -->    <mvc:argument-resolvers>    <bean class="com.guorucheng.common.annotation.resolver.CurrentTenantKeyResolver" />    </mvc:argument-resolvers>        <!-- FastJson注入 -->        <mvc:message-converters register-defaults="true">            <!-- 避免IE执行AJAX时,返回JSON出现下载文件 -->            <!-- FastJson -->            <bean id="fastJsonHttpMessageConverter"                  class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">                <property name="supportedMediaTypes">                    <list>                        <!-- 这里顺序不能反,一定先写text/html,不然ie下出现下载提示 -->                        <value>text/html;charset=UTF-8</value>                        <value>application/json;charset=UTF-8</value>                    </list>                </property>                <property name="features">                    <array value-type="com.alibaba.fastjson.serializer.SerializerFeature">                        <!-- 避免循环引用 -->                        <value>DisableCircularReferenceDetect</value>                        <!-- 是否输出值为null的字段 -->                        <value>WriteMapNullValue</value>                        <!-- 数值字段如果为null,输出为0,而非null -->                        <value>WriteNullNumberAsZero</value>                        <!-- 字符类型字段如果为null,输出为"",而非null  -->                        <value>WriteNullStringAsEmpty</value>                        <!-- List字段如果为null,输出为[],而非null -->                        <value>WriteNullListAsEmpty</value>                        <!-- Boolean字段如果为null,输出为false,而非null -->                        <value>WriteNullBooleanAsFalse</value>                    </array>                </property>            </bean>        </mvc:message-converters>    </mvc:annotation-driven>    <!-- 静态资源配置 -->    <mvc:resources mapping="/resources/**" location="/resources/"/>    <!-- 对模型视图名称的解析,即在模型视图名称添加前后缀 -->    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">        <property name="prefix" value="/WEB-INF/pages/"/>        <property name="suffix" value=".jsp"/>    </bean>    <!-- 上传限制 -->    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">        <!-- 上传文件大小限制为31M,31*1024*1024 -->        <property name="maxUploadSize" value="32505856"/>    </bean></beans>

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD SQL Map Config 3.0//EN"        "http://mybatis.org/dtd/mybatis-3-config.dtd"><!-- |   plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下: |   properties?, settings?, |   typeAliases?, typeHandlers?, |   objectFactory?,objectWrapperFactory?, |   plugins?, |   environments?, databaseIdProvider?, mappers? |--><configuration>    <!--     | 全局配置设置     |     | 可配置选项                   默认值,     描述     |     | aggressiveLazyLoading       true,     当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。     | multipleResultSetsEnabled   true,     允许和不允许单条语句返回多个数据集(取决于驱动需求)     | useColumnLabel              true,     使用列标签代替列名称。不同的驱动器有不同的作法。参考一下驱动器文档,或者用这两个不同的选项进行测试一下。     | useGeneratedKeys            false,    允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。     | autoMappingBehavior         PARTIAL,  指定MyBatis 是否并且如何来自动映射数据表字段与对象的属性。PARTIAL将只自动映射简单的,没有嵌套的结果。FULL 将自动映射所有复杂的结果。     | defaultExecutorType         SIMPLE,   配置和设定执行器,SIMPLE 执行器执行其它语句。REUSE 执行器可能重复使用prepared statements 语句,BATCH执行器可以重复执行语句和批量更新。     | defaultStatementTimeout     null,     设置一个时限,以决定让驱动器等待数据库回应的多长时间为超时     | -->    <settings>        <!-- 这个配置使全局的映射器启用或禁用缓存 -->        <setting name="cacheEnabled" value="true"/>        <!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载 -->        <setting name="lazyLoadingEnabled" value="true"/>        <setting name="multipleResultSetsEnabled" value="true"/>        <setting name="useColumnLabel" value="true"/>        <setting name="defaultExecutorType" value="REUSE"/>        <setting name="defaultStatementTimeout" value="25000"/>    </settings></configuration>

dataSource.xml

<?xml version="1.0" encoding="UTF-8"?><tenants><tenant><!-- 租户,租户域名(项目中必须唯一)--><tenantKey>www.guorucheng.com;app.guorucheng.com</tenantKey><dataSource><url><![CDATA[jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf8&autoReconnect=true]]></url><!-- 必须的 --><username>root</username><!-- 必须的 --><password>root</password><!-- 必须的 --><initialSize>0</initialSize><!-- 初始化连接大小 --><maxActive>5</maxActive><!-- 连接池最大使用连接数量 --><minIdle>0</minIdle><!-- 连接池最小空闲 --><maxWait>60000</maxWait><!-- 获取连接最大等待时间 --><validationQuery>SELECT 1</validationQuery><testOnBorrow>false</testOnBorrow><testOnReturn>false</testOnReturn><testWhileIdle>true</testWhileIdle><timeBetweenEvictionRunsMillis>60000</timeBetweenEvictionRunsMillis><!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --><minEvictableIdleTimeMillis>25200000</minEvictableIdleTimeMillis><!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --></dataSource></tenant><tenant><!-- 租户,租户域名(项目中必须唯一) --><tenantKey>beijing.guorucheng.com;app.beijing.guorucheng.com</tenantKey><dataSource><url><![CDATA[jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=utf8&autoReconnect=true]]></url><!-- 必须的 --><username>root</username><!-- 必须的 --><password>root</password><!-- 必须的 --><initialSize>0</initialSize><!-- 初始化连接大小 --><maxActive>5</maxActive><!-- 连接池最大使用连接数量 --><minIdle>0</minIdle><!-- 连接池最小空闲 --><maxWait>60000</maxWait><!-- 获取连接最大等待时间 --><validationQuery>SELECT 1</validationQuery><testOnBorrow>false</testOnBorrow><testOnReturn>false</testOnReturn><testWhileIdle>true</testWhileIdle><timeBetweenEvictionRunsMillis>60000</timeBetweenEvictionRunsMillis><!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --><minEvictableIdleTimeMillis>25200000</minEvictableIdleTimeMillis><!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --></dataSource></tenant></tenants>

web.xml

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/javaee"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"id="WebApp_ID" version="3.0" ><!-- 加载Spring配置文件 --><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring.xml</param-value></context-param><!-- 字符集 过滤器 --><filter><filter-name>encodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param></filter><filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><!-- Spring监听器 --><listener><description>Spring监听器</description><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><!-- 防止Spring内存溢出监听器 --><listener><listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class></listener><!-- Spring MVC --><servlet><servlet-name>SpringMVC</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><description>SpringMVC</description><param-name>contextConfigLocation</param-name><param-value>classpath:spring-mvc.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>SpringMVC</servlet-name><url-pattern>/</url-pattern></servlet-mapping><!-- Session超时时间 --><session-config><session-timeout>15</session-timeout></session-config></web-app>

pom.xml

<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><groupId>com.guorucheng</groupId><artifactId>DynamicDataSource</artifactId><version>0.0.1-GRC</version><packaging>war</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><spring.version>4.2.5.RELEASE</spring.version><junit.version>4.12</junit.version><druid.version>1.1.0</druid.version><fastjson.version>1.2.8</fastjson.version><mybaitsplus.version>2.1-gamma</mybaitsplus.version><velocity.version>1.7</velocity.version><mysql.version>5.1.38</mysql.version><log4j.version>1.2.17</log4j.version><slf4j.version>1.7.19</slf4j.version><aspectjweaver.version>1.8.8</aspectjweaver.version><fileupload.version>1.3.1</fileupload.version><jstl.version>1.2</jstl.version><dom4j.version>1.6.1</dom4j.version><freemarker.version>2.3.26-incubating</freemarker.version></properties><dependencies><!-- JUnit --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>${junit.version}</version><scope>test</scope></dependency><dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>${freemarker.version}</version></dependency><!-- dom4j --><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>${dom4j.version}</version></dependency><!-- Spring --><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>${spring.version}</version><type>jar</type><scope>compile</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version><type>jar</type><scope>compile</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>${spring.version}</version><type>jar</type><scope>compile</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>${spring.version}</version><type>jar</type><scope>compile</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>${spring.version}</version><type>jar</type><scope>compile</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>${spring.version}</version><type>jar</type><scope>compile</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>${spring.version}</version><type>jar</type><scope>compile</scope></dependency><!-- Spring MVC --><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>${spring.version}</version><type>jar</type><scope>compile</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>${spring.version}</version><type>jar</type><scope>compile</scope></dependency><!-- AOP --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>${aspectjweaver.version}</version></dependency><!-- FileUpload --><dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>${fileupload.version}</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>jstl</artifactId><version>${jstl.version}</version></dependency><!-- Mybatis-Plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus</artifactId><version>${mybaitsplus.version}</version></dependency><dependency><groupId>org.apache.velocity</groupId><artifactId>velocity</artifactId><version>${velocity.version}</version></dependency><!-- Mysql --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.version}</version></dependency><!-- Druid --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>${druid.version}</version></dependency><!-- FastJson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>${fastjson.version}</version></dependency><!-- Log --><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>${log4j.version}</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>${slf4j.version}</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>${slf4j.version}</version></dependency></dependencies><build><finalName>DynamicDataSource</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.7</source><target>1.7</target></configuration></plugin></plugins></build></project>


下载项目部署和mybatis-plus简介

# Mybatis-Plus DynamicDataSource运行方法:1. 建立数据库,导入SQL(在resources的sql中)。2. 引入本Maven项目,修改数据库配置文件。3. 添加Web服务器,运行。4. 修改localhost解析---127.0.0.1 www.guorucheng.com---127.0.0.1 app.guorucheng.com---127.0.0.1 beijing.guorucheng.com---127.0.0.1 app.beijing.guorucheng.com5.浏览器地址栏访问:http://www.guorucheng.com:8080/DynamicDataSource/sysUser/findSysUsershttp://app.guorucheng.com:8080/DynamicDataSource/sysUser/findSysUsershttp://beijing.guorucheng.com:8080/DynamicDataSource/sysUser/findSysUsershttp://app.beijing.guorucheng.com:8080/DynamicDataSource/sysUser/findSysUsers查看数据库切换效果。> 为简化开发工作、提高生产率而生# 简介 | IntroMybatis 增强工具包 - 只做增强不做改变,简化`CRUD`操作# 优点 | Advantages- **纯正血统**:完全继承原生 `Mybatis` 的所有特性- **最少依赖**:仅仅依赖`Mybatis`以及`Mybatis-Spring`- **性能损耗小**:启动即会自动注入基本CURD ,性能无损耗,直接面向对象操作- **自动热加载**:Mapper对应的xml可以热加载,大大减少重启Web服务器时间,提升开发效率- **自动生成代码**:包含自动生成代码类以及Maven插件,通过少量配置,即可快速生成Mybatis对应的xml、mapper、entity、service、serviceimpl层代码,减少开发时间- **自定义操作**:支持自定义Sql注入,实现个性化操作- **自定义转义规则**:支持数据库关键词(例如:`order`、`key`等)自动转义,支持自定义关键词- **多种主键策略**:支持多达4种主键策略,可自由配置,若无将会自动填充,更有充满黑科技的`分布式全局唯一ID生成器`- **无缝分页插件**:基于Mybatis物理分页,无需关心具体操作,等同于编写基本`selectList`查询- **性能分析**:自带Sql性能分析插件,开发测试时,能有效解决慢查询- **全局拦截**:提供全表`delete`、`update`操作智能分析阻断- **避免Sql注入**:内置Sql注入内容剥离器,预防Sql注入攻击# 文档 | Documentation[中文](http://mp.baomidou.com/) | [English](http://mp.baomidou.com/en/)# 原理 | Principle[Mybatis-Plus 实践及架构原理](http://git.oschina.net/baomidou/mybatis-plus/attach_files)



阅读全文
0 0
原创粉丝点击