springMvc结合hibernate多数据源和多事务管理器
这个示例使用的spring 版本是4.3.3,hibernate版本是4.1.12,ide是idea2016,数据库是mysql和sqlserver2008。
要到达的效果是:
1、可以使用注解在服务层选择数据源@DataSource
2、使用事务注解@Transactional选择不同的事务管理器
1.数据源是相同类型的数据库: 一个SessionFactory+动态数据源+一个事务管理器
2.数据源是不同类型的数据库: 根据类型配置多套SessionFactory
一、这里要配置三个数据源,每个数据库都只有一张表,每张表都只有id和name这两列。
sqlserver的数据源
@Bean public DataSource sqlServerSource() { BasicDataSource ds = new BasicDataSource(); ds.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); ds.setUrl("jdbc:sqlserver://localhost:1433;DatabaseName=testHibernate;SelectMethod=Cursor"); ds.setUsername("sa"); ds.setPassword("sa123"); return ds; }
mysql数据源分别是两个不同的数据库
@Bean public DataSource dataSource() { BasicDataSource ds = new BasicDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306/myspittles"); ds.setUsername("root"); ds.setPassword("root"); return ds; } @Bean public DataSource dataSourceHi2() { BasicDataSource ds = new BasicDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306/hiberate2"); ds.setUsername("root"); ds.setPassword("root"); return ds; }
二、SessionFactory工厂配置两个一个是myslq的作为默认的SessionFactory,一个是sqlserver的:
mysql SessionFactory:
@Bean public SessionFactory sessionFactoryMySql() { try { LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean(); lsfb.setDataSource(dynamicDataSource()); lsfb.setPackagesToScan("com.model");//扫描时实体类所在的路径 Properties props = new Properties(); props.setProperty("dialect", "org.hibernate.dialect.MySQL5Dialect");//hibernate对mysql的方言 props.setProperty("show_sql", "true"); props.setProperty("current_session_context_class", "thread"); lsfb.setHibernateProperties(props); lsfb.afterPropertiesSet(); SessionFactory object = lsfb.getObject(); return object; } catch (IOException e) { return null; } }
sqlserver SessionFactory:这个是在事务中进行切换
public SessionFactory sessionFactorySqlServer() { try { LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean(); lsfb.setDataSource(dynamicDataSource()); lsfb.setPackagesToScan("com.model");//扫描时实体类所在的路径 Properties props = new Properties(); props.setProperty("dialect", "org.hibernate.dialect.SQLServer2008Dialect");//hibernate对sqlserver的方言 props.setProperty("show_sql", "true"); props.setProperty("current_session_context_class", "thread"); lsfb.setHibernateProperties(props); lsfb.afterPropertiesSet(); SessionFactory object = lsfb.getObject(); return object; } catch (IOException e) { return null; } }
对应有两个事务管理器,一个是默认事务管理器,一个是sqlserver的事务管理器:
默认事务管理器(mysql):
/** * myqls数据库的事务管理器 * * @return */ @Bean public PlatformTransactionManager annotation1() { System.out.println(sessionFactoryMySql()); HibernateTransactionManager transactionManager = new HibernateTransactionManager(); transactionManager.setSessionFactory(sessionFactoryMySql()); return transactionManager; }sqlserver的事务管理器:在使用sqlserver数据库的时侯 @Transactional(value = "SqlServer")就能选择事务
/** * SqlServer数据库的事务管理器 * * @return */ @Bean(name = "SqlServer") public PlatformTransactionManager annotation2() { System.out.println(sessionFactorySqlServer()); HibernateTransactionManager transactionManager = new HibernateTransactionManager(); transactionManager.setSessionFactory(sessionFactorySqlServer()); return transactionManager; }
配置动态数据源:
@Bean public DynamicDataSource dynamicDataSource() { DynamicDataSource source = new DynamicDataSource(); source.setDefaultTargetDataSource(dataSource()); Map<Object, Object> targetDataSources = new HashMap<Object, Object>(); targetDataSources.put("hi1", dataSourceHi1()); targetDataSources.put("hi2", dataSourceHi2()); targetDataSources.put("sqlServer", sqlServerSource()); targetDataSources.put("oracle", oracleDataSource()); source.setTargetDataSources(targetDataSources); return source; }
二、数据源注解
声明数据源注解类:DataSource.java,java注解请:百度一下
/** * Created by IBM on 2018/4/20. */ @Target({ElementType.METHOD})//这个注解是应用在方法上 @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface DataSource { /** * 数据源的标识 * @return */ String value() default ""; }注解解析器类:
/** * 数据源注解解析器 * 这个解析器的主要功能,是解析目标方法上如果有DataSource注解,那么解析出这个注解中的value值(权限的值) * Created by IBM on 2018/4/20. */ public class DataSourcesParser { public static String parse(Class targetClass,String method)throws Exception{ /** * Class对象的getMethods和getDeclaredMethods都是获取类对象的方法 * getMethod():获取当前类及所有继承的父类的public修饰的方法。仅包括public * getDeclaredMethod():获取当前类的所有方法,包括public/private/protected/default修饰的方法。 */ String methodAccess=""; //Method[]methods=targetClass.getMethods(); Method[]methods1=targetClass.getDeclaredMethods(); for(Method m:methods1){ if(m.getName()==method){ if(m.isAnnotationPresent(DataSource.class)){ //得到方法上的注解 DataSource dataSource=m.getAnnotation(DataSource.class); methodAccess=dataSource.value(); } } } return methodAccess; } }
这样就能使用@DataSource注解了。
三、定义aop切面类来解析和切换数据源
切面配置类:AspectConfig.java
/** * Created by IBM on 2018/2/24. */ @Configuration @EnableAspectJAutoProxy public class AspectConfig { @Bean public DataSourceAspect dataSourceAspect(){ return new DataSourceAspect(); } }
切面类:
/** * Created by IBM on 2018/4/20. * 使用注解创建切面 * 定义切面 */ @Aspect @Order(2)//切面的执行顺序,使他能够在事务启动前执行 @Component public class DataSourceAspect { // 这里是关键点,把切面的连接点放在了我们的注解上 @Pointcut("@annotation(com.config.annontionConfig.DataSource)") public void controllerAspect() { } // 在这里定义前置切面 @Before("controllerAspect()") public void beforeMethod(JoinPoint joinPoint) throws Throwable { // 这里执行保存日志的动作 /** * 1.获取访问目标方法应该具备的权限 * 为解析目标方法的PrivilegeInfo注解,根据我们定义的解析器,需要得到:目标类的class形式 方法的名称 */ Class targetClass = joinPoint.getTarget().getClass(); String methodName = joinPoint.getSignature().getName(); //得到该方法的访问权限 String methodAccess = DataSourcesParser.parse(targetClass, methodName);//获取数据源注解解析器解析出来的数据源 DynamicDataSourceHolder.setDataSource(methodAccess);//切换数据源 String a= DynamicDataSourceHolder.getDataSource(); System.out.println("......"+methodAccess+"......"); } //@After("controllerAspect()") public void afterMethod(JoinPoint joinPoint)throws Throwable{ // 这里执行保存日志的动作 /** * 1.获取访问目标方法应该具备的权限 * 为解析目标方法的PrivilegeInfo注解,根据我们定义的解析器,需要得到:目标类的class形式 方法的名称 */ Class targetClass = joinPoint.getTarget().getClass(); String methodName = joinPoint.getSignature().getName(); //得到该方法的访问权限 String methodAccess = DataSourcesParser.parse(targetClass, methodName); System.out.println("......"+methodAccess+"......"); } }动态数据源类:
/** * Created by IBM on 2018/2/7. */ public class DynamicDataSource extends AbstractRoutingDataSource { /** * 获得数据源 */ @Override protected Object determineCurrentLookupKey() { // return DynamicDataSourceHolder.getDataSource(); } }
/** * 创建DynamicDataSourceHolder用于持有当前线程中使用的数据源标识 * Created by IBM on 2018/2/7. */ public class DynamicDataSourceHolder { /** * 注意:数据源标识保存在线程变量中,避免多线程操作数据源时互相干扰 */ private static final ThreadLocal<String> THREAD_DATA_SOURCE = new ThreadLocal<String>(); // 获取数据源类型 public static String getDataSource() { return THREAD_DATA_SOURCE.get(); } // 设置数据源类型 public static void setDataSource(String dataSource) { THREAD_DATA_SOURCE.set(dataSource); } // 清除数据源类型 public static void clearDataSource() { THREAD_DATA_SOURCE.remove(); } }
有了上面这些就能够在服务层中切换不同的数据源:
sqlserver数据库
@DataSource(value = "sqlServer") @Override @Transactional(value = "SqlServer") public SqlServer findById(int id) { return repository.findById(id); }
mysql 的hiberate2数据库
@DataSource("hi2") @Override @Transactional public void save2(Hiber2 hiber2) { String a = DynamicDataSourceHolder.getDataSource(); repository.save2(hiber2); }
@DataSource("hi2")会在进入事务前先进入上面的切面,所以能够切换数据源。
切换了数据源后使用@Transactional(value = "SqlServer")切换事务,也就是切换hibernate。
项目的截图:
项目的源码:
https://download.****.net/download/u014572215/10370883点击打开链接