We often encounter problems with multiple data sources in projects, especially projects such as data synchronization or timing tasks. The most troublesome thing about multiple data sources is not to configure multiple data sources, but how to switch data sources in a flexible and dynamic manner. For example, in a spring and hibernate framework project, we often configure a dataSource in spring configuration to connect to the database, and then bind it to the sessionFactory, and then specify the sessionFactory in the dao layer code to perform database operations.
As shown in the figure above, each block is bound to be bound. If it is multiple data sources, it can only be the same as in the figure below.
It can be seen that two SessionFactory have been written in the Dao layer code. In this way, if there is another data source in the future, you have to change the code and add a SessionFactory. Obviously, this does not conform to the principle of opening and closing.
Then the correct way to do it should be
The code is as follows:
1. applicationContext.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:aop="http://www.springframework.org/schema/aop" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:jms="http://www.springframework.org/schema/jms" xmlns:lang="http://www.springframework.org/schema/lang" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:oxm="http://www.springframework.org/schema/oxm" xmlns:p="http://www.springframework.org/schema/p" xmlns:task="http://www.springframework.org/schema/task" xmlns:tx="http://www.springframework.org/schema/tx" 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/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.1.xsd http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.1.xsd http://www.springframework.org/schema/jms/spring-jms-3.1.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.1.xsd http://www.springframework.org/schema/oxm/spring-oxm-3.1.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd"> <context:annotation-config /> <context:component-scan base-package="com"></context:component-scan> <bean> <property name="locations"> <list> <value>classpath:com/resource/config.properties</value> </list> </property> </bean> <bean id="dataSourceOne" destroy-method="close"> <property name="driverClass" value="${dbOne.jdbc.driverClass}" /> <property name="jdbcUrl" value="${dbOne.jdbc.url}" /> <property name="user" value="${dbOne.jdbc.user}" /> <property name="password" value="${dbOne.jdbc.password}" /> <property name="initialPoolSize" value="${dbOne.jdbc.initialPoolSize}" /> <property name="minPoolSize" value="${dbOne.jdbc.minPoolSize}" /> <property name="maxPoolSize" value="${dbOne.jdbc.maxPoolSize}" /> </bean> <bean id="dataSourceTwo" destroy-method="close"> <property name="driverClass" value="${dbTwo.jdbc.driverClass}" /> <property name="jdbcUrl" value="${dbTwo.jdbc.url}" /> <property name="user" value="${dbTwo.jdbc.user}" /> <property name="password" value="${dbTwo.jdbc.password}" /> <property name="initialPoolSize" value="${dbTwo.jdbc.initialPoolSize}" /> <property name="minPoolSize" value="${dbTwo.jdbc.minPoolSize}" /> <property name="maxPoolSize" value="${dbTwo.jdbc.maxPoolSize}" /> </bean> <bean id="dynamicDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry value-ref="dataSourceOne" key="dataSourceOne"></entry> <entry value-ref="dataSourceTwo" key="dataSourceTwo"></entry> </map> </property> <property name="defaultTargetDataSource" ref="dataSourceOne"> </property> </bean> <bean id="sessionFactory"> <property name="dataSource" ref="dynamicDataSource" /> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</prop> <prop key="hibernate.show_sql">false</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hbm2ddl.auto">create</prop> </props> </property> <property name="packagesToScan"> <list> <value>com.po</value> </list> </property> </bean> <bean id="transactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <aop:config> <aop:pointcut id="transactionPointCut" expression="execution(* com.dao..*.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="transactionPointCut" /> </aop:config> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="add*" propagation="REQUIRED" /> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="delete*" propagation="REQUIRED" /> <tx:method name="*" read-only="true" /> </tx:attributes> </tx:advice> <aop:config> <aop:aspect id="dataSourceAspect" ref="dataSourceIntercept"> <aop:pointcut id="daoOne" expression="execution(* com.dao.one.*.*(..))" /> <aop:pointcut id="daoTwo" expression="execution(* com.dao.two.*.*(..))" /> <aop:before pointcut-ref="daoOne" method="setdataSourceOne" /> <aop:before pointcut-ref="daoTwo" method="setdataSourceTwo" /> </aop:aspect> </aop:config> </beans> 2. DynamicDataSource.class
package com.core; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource{ @Override protected Object determineCurrentLookupKey() { return DatabaseContextHolder.getCustomerType(); } } 3. DatabaseContextHolder.class
package com.core; public class DatabaseContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static void setCustomerType(String customerType) { contextHolder.set(customerType); } public static String getCustomerType() { return contextHolder.get(); } public static void clearCustomerType() { contextHolder.remove(); } } 4. DataSourceInterceptor.class
package com.core; import org.aspectj.lang.JoinPoint; import org.springframework.stereotype.Component; @Component public class DataSourceInterceptor { public void setdataSourceOne(JoinPoint jp) { DatabaseContextHolder.setCustomerType("dataSourceOne"); } public void setdataSourceTwo(JoinPoint jp) { DatabaseContextHolder.setCustomerType("dataSourceTwo"); } } 5. po entity class
package com.po; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "BTSF_BRAND", schema = "hotel") public class Brand { private String id; private String names; private String url; @Id @Column(name = "ID", unique = true, nullable = false, length = 10) public String getId() { return this.id; } public void setId(String id) { this.id = id; } @Column(name = "NAMES", nullable = false, length = 50) public String getNames() { return this.names; } public void setNames(String names) { this.names = names; } @Column(name = "URL", length = 200) public String getUrl() { return this.url; } public void setUrl(String url) { this.url = url; } } package com.po; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "CITY", schema = "car") public class City { private Integer id; private String name; @Id @Column(name = "ID", unique = true, nullable = false) public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(name = "NAMES", nullable = false, length = 50) public String getName() { return name; } public void setName(String name) { this.name = name; } } 6. BrandDaoImpl.class
package com.dao.one; import java.util.List; import javax.annotation.Resource; import org.hibernate.Query; import org.hibernate.SessionFactory; import org.springframework.stereotype.Repository; import com.po.Brand; @Repository public class BrandDaoImpl implements IBrandDao { @Resource protected SessionFactory sessionFactory; @SuppressWarnings("unchecked") @Override public List<Brand> findAll() { String hql = "from Brand"; Query query = sessionFactory.getCurrentSession().createQuery(hql); return query.list(); } } 7. CityDaoImpl.class
package com.dao.two; import java.util.List; import javax.annotation.Resource; import org.hibernate.Query; import org.hibernate.SessionFactory; import org.springframework.stereotype.Repository; import com.po.City; @Repository public class CityDaoImpl implements ICityDao { @Resource private SessionFactory sessionFactory; @SuppressWarnings("unchecked") @Override public List<City> find() { String hql = "from City"; Query query = sessionFactory.getCurrentSession().createQuery(hql); return query.list(); } } 8. DaoTest.class
package com.test; import java.util.List; import javax.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.transaction.TransactionConfiguration; import com.dao.one.IBrandDao; import com.dao.two.ICityDao; import com.po.Brand; import com.po.City; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:com/resource/applicationContext.xml") @TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = false) public class DaoTest { @Resource private IBrandDao brandDao; @Resource private ICityDao cityDao; @Test public void testList() { List<Brand> brands = brandDao.findAll(); System.out.println(brands.size()); List<City> cities = cityDao.find(); System.out.println(cities.size()); } } Use aop to achieve the purpose of dynamically changing the data source. When we need to add data sources, we only need to add aop configuration in the applicationContext configuration file and create a new DataSourceInterceptor. Without changing any code.
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.