需求是根據不一樣的用戶分配不一樣的數據源,並且數據源最好可編輯,實現動態化。那最好的方案確定是把數據源信息存數據庫裏啊。 因而搜了好多文章,找到了這篇文章 但文章中有點問題,一直不走寫的代碼,發現有一點寫錯了,或者是配置沒寫全的緣故,並且缺乏一個文件,就在原來的基礎上稍微修改了一下。java
主要配置文件applicationContext.xml,不關鍵的已省略。mysql
<!--多數據源切換管理--> <bean id="dynamicDataSource" class="com.rongtai.acs.core.utils.DynamicDataSource"> <property name="targetDataSources"> <map> </map> </property> <!--默認數據源--> <property name="defaultTargetDataSource" ref="dataSource" /> </bean> <!-- local development環境 --> <beans profile="development"> <context:property-placeholder ignore-resource-not-found="true" location="classpath*:/application.properties" /> <!-- Tomcat JDBC鏈接池 --> <bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <property name="defaultAutoCommit" value="false" /> <!-- 默認值是true,當從鏈接池取鏈接時,驗證這個鏈接是否有效--> <property name="testOnBorrow" value="true"/> <!--一條sql語句,用來驗證數據庫鏈接是否正常。這條語句必須是一個查詢模式,並至少返回一條數據。能夠爲任何能夠驗證數據庫鏈接是否正常的sql--> <property name="validationQuery" value="select 1"/> <!-- 是否自動回收超時鏈接--> <property name="removeAbandoned" value="true"/> <!-- 空閒時測試鏈接,必須配置validationQuery纔有效--> <property name="testWhileIdle" value="true"/> <!-- 鏈接池啓動時的初始值 --> <property name="initialSize" value="8"/> <!-- 鏈接Idle一個小時後超時 --> <property name="timeBetweenEvictionRunsMillis" value="3600000" /> <property name="minEvictableIdleTimeMillis" value="3600000" /> </bean> <!-- Jpa Entity Manager 配置 --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dynamicDataSource"/> <property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter"/> <property name="packagesToScan" value="com.rongtai.acs"/> <property name="jpaProperties"> <props> <!-- 命名規則 My_NAME->MyName --> <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> <!--<prop key="hibernate.hbm2ddl.auto">create-drop</prop>--> <prop key="hibernate.hbm2ddl.import_files">sql/mysql/init.sql</prop> <prop key="hibernate.hbm2ddl.import_files_sql_extractor"> org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor </prop> <prop key="hibernate.connection.useUnicode">true</prop> <prop key="hibernate.connection.characterEncoding">UTF-8</prop> <prop key="hibernate.connection.charSet">UTF-8</prop> </props> </property> </bean> </beans>
類一 DynamicDataSource.javaspring
package com.rongtai.acs.core.utils; import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup; import javax.sql.DataSource; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Map; public class DynamicDataSource extends AbstractRoutingDataSource { private Logger log = LoggerFactory.getLogger(this.getClass()); private Map<Object, Object> _targetDataSources; /** * @see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#determineCurrentLookupKey() * @describe 數據源爲空或者爲0時,自動切換至默認數據源,即在配置文件中定義的dataSource數據源 */ @Override protected Object determineCurrentLookupKey() { String dataSourceName = DbContextHolder.getDBType(); if (dataSourceName == null) { dataSourceName = Constants.DEFAULT_DATA_SOURCE_NAME; } else { this.selectDataSource(dataSourceName); } log.debug("--------> use datasource " + dataSourceName); return dataSourceName; } /** * 到數據庫中查找名稱爲dataSourceName的數據源 * * @author Geloin * @date Jan 20, 2014 12:15:41 PM * @param dataSourceName */ private void selectDataSource(String dataSourceName) { Object sid = DbContextHolder.getDBType(); if (StringUtils.isEmpty(dataSourceName) || dataSourceName.trim().equals("dataSource")) { DbContextHolder.setDBType("dataSource"); return; } Object obj = this._targetDataSources.get(dataSourceName); if (obj != null && sid.equals(dataSourceName)) { return; } else { DataSource dataSource = this.getDataSource(dataSourceName); if (null != dataSource) { this.setDataSource(dataSourceName, dataSource); } } } @Override public void setTargetDataSources(Map<Object, Object> targetDataSources) { this._targetDataSources = targetDataSources; super.setTargetDataSources(this._targetDataSources); afterPropertiesSet(); } private void addTargetDataSource(String key, DataSource dataSource) { this._targetDataSources.put(key, dataSource); this.setTargetDataSources(this._targetDataSources); } private DataSource createDataSource(String driverClassName, String url, String username, String password) { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName(driverClassName); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } /** * 到數據庫中查詢名稱爲dataSourceName的數據源 * * @author Geloin * @date Jan 20, 2014 12:18:12 PM * @param dataSourceName * @return */ private DataSource getDataSource(String dataSourceName) { this.selectDataSource(Constants.DEFAULT_DATA_SOURCE_NAME); this.determineCurrentLookupKey(); Connection conn = null; try { conn = this.getConnection(); StringBuilder builder = new StringBuilder(); builder.append("SELECT C_NAME,C_TYPE,C_URL,C_USER_NAME,"); builder.append("C_PASSWORD,C_JNDI_NAME,C_DRIVER_CLASS_NAME "); builder.append("FROM IA_DATA_SOURCE WHERE c_name = ?"); PreparedStatement ps = conn.prepareStatement(builder.toString()); ps.setString(1, dataSourceName); ResultSet rs = ps.executeQuery(); if (rs.next()) { Integer type = rs.getInt("C_TYPE"); if (StringUtils.isNotEmpty(String.valueOf(type))) { // DB String url = rs.getString("C_URL"); String userName = rs.getString("C_USER_NAME"); String password = rs.getString("C_PASSWORD"); String driverClassName = rs .getString("C_DRIVER_CLASS_NAME"); DataSource dataSource = this.createDataSource( driverClassName, url, userName, password); return dataSource; } else { // JNDI String jndiName = rs.getString("C_JNDI_NAME"); JndiDataSourceLookup jndiLookUp = new JndiDataSourceLookup(); DataSource dataSource = jndiLookUp.getDataSource(jndiName); return dataSource; } } rs.close(); ps.close(); } catch (SQLException e) { log.error(String.valueOf(e)); } finally { try { conn.close(); } catch (SQLException e) { log.error(String.valueOf(e)); } } return null; } /** * 將已存在的數據源存儲到內存中 * * @author Geloin * @date Jan 20, 2014 12:24:13 PM * @param dataSourceName * @param dataSource */ private void setDataSource(String dataSourceName, DataSource dataSource) { this.addTargetDataSource(dataSourceName, dataSource); DbContextHolder.setDBType(dataSourceName); } }
類二 DbContextHolder.javasql
package com.rongtai.acs.core.utils; public class DbContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static void setDBType(String dbType) { contextHolder.set(dbType); } public static String getDBType() { return (String) contextHolder.get(); } public static void clearDBType() { contextHolder.remove(); } /** * 切換數據源語句 字符串爲實體類GsoftDataSource中的name屬性也就是數據庫表IA_DATA_SOURCE中的c_name字段 * DbContextHolder.setDBType("dataSourceName"); */ }
類三 Constants.java數據庫
package com.rongtai.acs.core.utils; public class Constants { public static String DEFAULT_DATA_SOURCE_NAME="dataSource"; public static String DataSourceType=""; }
類四 實體類 GsoftDataSource.javaapache
package com.rongtai.acs.core.utils; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "IA_DATA_SOURCE") public class GsoftDataSource { @Id // @SequenceGenerator(name = "IA_DATA_SOURCE_SEQ", sequenceName = "IA_DATA_SOURCE_SEQ", allocationSize = 1) // @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "IA_DATA_SOURCE_SEQ") private Long id; /** * data source name */ @Column(name = "C_NAME", unique = true) private String name; /** * data source type, default is database<br /> */ @Column(name = "C_TYPE") private Integer type = DataSourceType.DB.intValue(); /** * 數據庫類型,目前只支持MySql和Oracle<br /> */ @Column(name = "C_DATA_TYPE") private Integer dataType = DataType.ORACLE.intValue(); @Column(name = "C_URL") private String url; @Column(name = "C_USER_NAME") private String userName; @Column(name = "C_PASSWORD") private String password; @Column(name = "C_JNDI_NAME") private String jndiName; @Column(name = "C_DRIVER_CLASS_NAME") private String driverClassName; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Integer getType() { return type; } public void setType(Integer type) { this.type = type; } 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 getJndiName() { return jndiName; } public void setJndiName(String jndiName) { this.jndiName = jndiName; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDriverClassName() { return driverClassName; } public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; } public Integer getDataType() { return dataType; } public void setDataType(Integer dataType) { this.dataType = dataType; } public enum DataType { ORACLE(0), MYSQL(1); private Integer value; public Integer intValue() { return this.value; } DataType(Integer value) { this.value = value; } } public enum DataSourceType { DB(0), ss(1); private Integer value; DataSourceType(Integer value) { this.value = value; } public Integer intValue() { return this.value; } } }
實體類須要建對應的數據庫表,因爲我只用到了mysql,只能只說它了,sql語句以下:tomcat
CREATE TABLE `ia_data_source` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `c_data_type` INT(11) NOT NULL, `c_driver_class_name` VARCHAR(255) DEFAULT NULL, `c_jndi_name` VARCHAR(255) DEFAULT NULL, `c_name` VARCHAR(255) DEFAULT NULL, `c_password` VARCHAR(255) DEFAULT NULL, `c_type` INT(11) NOT NULL, `c_url` VARCHAR(255) DEFAULT NULL, `c_user_name` VARCHAR(255) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `UK_bo3uh3stkpnq52ffugvt3934r` (`c_name`) ) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
表數據截下圖app
切換數據源語句,只需在service對數據進行增刪改查時加上如下語句ide
DbContextHolder.setDBType("IA_DATA_SOURCE中某個數據源的name屬性的值");
個人是測試
分析:原來文章的bug是不能加載加入的這些文件,緣由是在Jpa Entity Manager 配置的地方,DataSource沒有用配置好的數據源,這是必要的;另外是缺乏Constants.java類,害的我揣摩了好久。
須要注意的是包名若是更改的話,記得配置文件中也要把對應的Class類路徑作下更改。
原本要實現的是在用戶登陸時根據不一樣的用戶類型,選擇不一樣的數據源,還未實現。先實現了這個demo,等完成時,再貼代碼。
歡迎前來交流 ^_^ !