====== Spring+Mybatis 多数据源配置_2 ====== ===== 方法描述 ===== 采用spring配置文件配置动态数据源,利于强大的AOP切面,根据service实现类的自定义注解或者指定参数判断,在spring事务管理获取动态数据源之前,设置我们希望连接的数据源,从而灵活配置 ===== 优缺点 ===== - 一套统一配置,通过注解或者入参来灵活判断设置数据源,代码级别判断,配置不需要动,方便可控 - 不仅适合两个数据库没有相关性的情况,而且还适合master-slave性的多数据源的配置 - vo,dao,mapper,service仅需要一套 ===== 碰到的坑 ===== - spring-mvc对controller的扫描路径一定要写到web不能把sevice包含进去,或者指定扫描@Controller注解,否则会把service扫进去,那spring事务管理扫面将不起作用 - 事务管理注解驱动[transaction]一定要配置在AOP切面[transactionPointcut](事务管理获取动态数据源)之前,否则注解会无效 - xml中如果已经配置事务transactionAdvice,service方法上可以不配置注解 - 如果在事务中抛出异常Exception,则会回滚,不需要配置rollback-for="Exception" - AOP切面[dataSourceAspect](动态设置数据源)一定要配置在AOP切面[transactionPointcut](事务管理获取动态数据源)之前,否则先获取完再设置数据源会无效 ===== 暂未解决 ===== 使用自定义注解打在service的方法上,不知道为啥,切面里获取不到注解。但是,换了一种方法,使用入参送入枚举,切面里获取入参判断枚举值来设置数据源,可以搞定! ===== 主要代码 ===== ==== spring-mvc.xml ==== UTF-8 ignore 0.## text/plain;charset=utf-8 html/text;charset=utf-8 text/html;charset=utf-8 text/json;charset=utf-8 application/json;charset=utf-8 atom=application/atom+xml html=text/html json=application/json xml=application/xml *=*/* ==== application-context.xml ==== ==== Datasource.java ==== package com.gxx.record.core; /** *
*
Title:
*
* none *
*
Description:
*
*

none *

*
* * @author Gxx * @version 1.0, 2016年1月12日 * @since record * */ public enum Datasource { //主库 MASTER, //备库 SLAVE }
==== DatabaseContextHolder.java ==== package com.gxx.record.core; /** *
*
Title:
*
* none *
*
Description:使用ThreadLocal设置和获取当前线程的数据源KEY
*
*

none *

*
* * @author Gxx * @version 1.0, 2016年1月12日 * @since record * */ public class DatabaseContextHolder { /** * ThreadLocal */ private static final ThreadLocal contextHolder = new ThreadLocal(); /** * 设置数据源KEY * @param customerType */ public static void setCustomerType(String customerType) { contextHolder.set(customerType); } /** * 获取数据源KEY * @return */ public static String getCustomerType() { return contextHolder.get(); } /** * 清空数据源KEY */ public static void clearCustomerType() { contextHolder.remove(); } }
==== DynamicDataSource.java ==== package com.gxx.record.core; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** *
*
Title:
*
* none *
*
Description:动态获取数据源KEY,继承父类AbstractRoutingDataSource
*
*

none *

*
* * @author Gxx * @version 1.0, 2016年1月12日 * @since record * */ public class DynamicDataSource extends AbstractRoutingDataSource{ /** * 获取数据源值KEY */ @Override protected Object determineCurrentLookupKey() { return DatabaseContextHolder.getCustomerType(); } }
==== DataSourceInterceptor.java ==== package com.gxx.record.core; import org.aspectj.lang.JoinPoint; import org.springframework.stereotype.Component; /** *
*
Title:
*
* none *
*
Description:数据源切面拦截器
*
*

none *

*
* * @author Gxx * @version 1.0, 2016年1月12日 * @since record * */ @Component public class DataSourceInterceptor { /** * 设置数据源KEy * @param jp */ public void setDataSource(JoinPoint jp) { /** * 清空数据源KEY */ DatabaseContextHolder.clearCustomerType(); /** * 这里不知道为啥,使用注册方式注解方法或者入参,这里取不到注解,才改成入参送指定枚举的方式 */ for(Object param : jp.getArgs()){ if(param instanceof Datasource){ Datasource datasource = (Datasource)param; if(Datasource.MASTER.name().equals(datasource.name())){ DatabaseContextHolder.setCustomerType(Datasource.MASTER.name()); } else if(Datasource.SLAVE.name().equals(datasource.name())){ DatabaseContextHolder.setCustomerType(Datasource.SLAVE.name()); } } } } }
==== UserService.java ==== package com.gxx.record.service; import com.gxx.record.base.vo.User; import com.gxx.record.core.Datasource; /** *
*
Title:
*
* 用户服务接口 *
*
Description:
*
*

none *

*
* * @author Administrator * @version 1.0, 2015年6月18日 * @since record * */ public interface UserService { /** * 新增用户 * @param datasource 使用枚举参数,动态设置数据源 * @param user */ public void doSaveUser(Datasource datasource, User user); /** * 根据姓名查用户 * @param datasource 使用枚举参数,动态设置数据源 * @param name * @return */ public User getUserByName(Datasource datasource, String name); }
==== UserServiceImpl.java ==== package com.gxx.record.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.gxx.record.base.dao.UserMapper; import com.gxx.record.base.vo.User; import com.gxx.record.core.Datasource; import com.gxx.record.service.UserService; /** *
*
Title:
*
* 用户服务实现类 *
*
Description:
*
*

none *

*
* * @author Administrator * @version 1.0, 2015年6月18日 * @since record * */ @Service("userService") public class UserServiceImpl implements UserService { @Autowired private UserMapper userDao; /** * 新增用户 * @param datasource 使用枚举参数,动态设置数据源 * @param user */ //xml中已经配置transactionAdvice,这里可以不配置注解 //@Transactional(readOnly=false, propagation=Propagation.REQUIRED) public void doSaveUser(Datasource datasource, User user){ userDao.insert(user); //如果在事务中抛出异常,则会回滚 //throw new RuntimeException("异常发生"); } /** * 根据姓名查用户 * @param datasource 使用枚举参数,动态设置数据源 * @param name * @return */ public User getUserByName(Datasource datasource, String name){ return userDao.getUserByName(name); } }
==== UserController.java ==== package com.gxx.record.web.user; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import com.gxx.record.base.vo.User; import com.gxx.record.core.Datasource; import com.gxx.record.dto.UserDto; import com.gxx.record.service.UserService; /** *
*
Title:
*
* none *
*
Description:用户控制器
*
*

none *

*
* * @author Administrator * @version 1.0, 2016年1月12日 * @since record * */ @Controller @RequestMapping("/user/") public class UserController { /** * 日志处理器 */ private final Logger logger = Logger.getLogger(UserController.class); @Autowired private UserService userService; @RequestMapping(value = "/preRegistFtl", method = RequestMethod.GET) public String preRegistFtl() { return "user/preRegistFtl"; } @RequestMapping(value = "/preRegistJsp", method = RequestMethod.GET) public String preRegistJsp() { return "user/preRegistJsp"; } /** * 注册 * @param request * @param userDto * @return */ @RequestMapping(value = "/registJsp",produces="application/json") public @ResponseBody UserDto registJsp(HttpServletRequest request, UserDto userDto) { logger.info("用户注册:主从=[" + userDto.getMasterOrSlave() + "],姓名=[" + userDto.getName() + "],密码=[" + userDto.getPassword() + "]"); /** * 1.判用户名是否存在 */ User user = null; if(StringUtils.equals(userDto.getMasterOrSlave(), UserDto.MASTER)){ user = userService.getUserByName(Datasource.MASTER, userDto.getName()); } else if(StringUtils.equals(userDto.getMasterOrSlave(), UserDto.SLAVE)){ user = userService.getUserByName(Datasource.SLAVE, userDto.getName()); } else { userDto.setSuccess(Boolean.FALSE.booleanValue()); userDto.setMessage("主还是从值[" + userDto.getMasterOrSlave() + "]有误!"); return userDto; } if(user != null){ userDto.setSuccess(Boolean.FALSE.booleanValue()); userDto.setMessage("用户名[" + userDto.getName() + "]已存在!"); return userDto; } /** * 2.创建用户对象 并 新增用户 */ user = new User(); user.setName(userDto.getName()); user.setPassword(userDto.getPassword()); user.setCreateDate("20150618"); user.setCreateTime("000000"); if(StringUtils.equals(userDto.getMasterOrSlave(), UserDto.MASTER)){ userService.doSaveUser(Datasource.MASTER, user); } else if(StringUtils.equals(userDto.getMasterOrSlave(), UserDto.SLAVE)){ userService.doSaveUser(Datasource.SLAVE, user); } /** * 3.返回结果 */ userDto.setSuccess(Boolean.TRUE.booleanValue()); userDto.setMessage("注册成功!"); return userDto; } /** * 注册 * @param request * @param userDto * @return */ @RequestMapping(value = "/registFtl") public String registFtl(HttpServletRequest request, UserDto userDto) { logger.info("用户注册:主从=[" + userDto.getMasterOrSlave() + "],姓名=[" + userDto.getName() + "],密码=[" + userDto.getPassword() + "]"); /** * 1.判用户名是否存在 */ User user = null; if(StringUtils.equals(userDto.getMasterOrSlave(), UserDto.MASTER)){ user = userService.getUserByName(Datasource.MASTER, userDto.getName()); } else if(StringUtils.equals(userDto.getMasterOrSlave(), UserDto.SLAVE)){ user = userService.getUserByName(Datasource.SLAVE, userDto.getName()); } else { userDto.setSuccess(Boolean.FALSE.booleanValue()); userDto.setMessage("主还是从值[" + userDto.getMasterOrSlave() + "]有误!"); return "user/result"; } if(user != null){ userDto.setSuccess(Boolean.FALSE.booleanValue()); userDto.setMessage("用户名[" + userDto.getName() + "]已存在,请更改用户名!"); return "user/result"; } /** * 2.创建用户对象 并 新增用户 */ user = new User(); user.setName(userDto.getName()); user.setPassword(userDto.getPassword()); user.setCreateDate("20150618"); user.setCreateTime("000000"); if(StringUtils.equals(userDto.getMasterOrSlave(), UserDto.MASTER)){ userService.doSaveUser(Datasource.MASTER, user); } else if(StringUtils.equals(userDto.getMasterOrSlave(), UserDto.SLAVE)){ userService.doSaveUser(Datasource.SLAVE, user); } /** * 3.返回结果 */ userDto.setSuccess(Boolean.TRUE.booleanValue()); userDto.setMessage("注册成功!"); return "user/result"; } }
===== 附上代码 ===== {{:分享:技术:数据源:record_dynamicdatasource.zip|}}