hibernate动态多数据源

本文详细记录了hibernate动态多数据源的配置过程,包括如何设置xml配置文件,创建动态数据源bean以及sessionFactory的定制。特别指出,hibernate在启动时仅对默认数据源自动建表,需要手动切换数据源并执行建表方法。此外,由于切换数据源只在当前线程生效,这可能导致操作上的不便。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

hibernate动态多数据源包括自动建表

网上搜了不少,只有多数据源的配置,但最主要的自动建表没有,我研究清楚记录下

hibernate 动态多数据源配置

hibernate的xml配置文件,设置多个数据源

<bean id="dataSourceOne" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> 
	     <property name="url" value="${hibernate.connection.url}" />
	     <property name="username"><value>${hibernate.connection.username}</value></property>
	     <property name="password" value="${hibernate.connection.password}" />
	     <property name="filters"><value>stat</value></property>
	     <property name="maxActive"><value>100</value></property>
	     <property name="initialSize"><value>1</value></property>
	     <property name="maxWait"><value>60000</value></property>
	     <property name="minIdle"><value>1</value></property>
	     <property name="timeBetweenEvictionRunsMillis"><value>60000</value></property>
	     <property name="minEvictableIdleTimeMillis"><value>300000</value></property>
	     <property name="validationQuery"><value>SELECT 'x'</value></property>
	     <property name="testWhileIdle"><value>true</value></property>
	     <property name="testOnBorrow"><value>false</value></property>
	     <property name="testOnReturn"><value>false</value></property>

	     <property name="poolPreparedStatements"><value>true</value></property>
	     <property name="maxOpenPreparedStatements"><value>20</value></property>
	 </bean>
	<bean id="dataSourceTwo" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> 
	     <property name="url" value="${hibernate.connection.url2}" />
	     <property name="username"><value>${hibernate.connection.username}</value></property>
	     <property name="password" value="${hibernate.connection.password}" />
	     <property name="filters"><value>stat</value></property>
	     <property name="maxActive"><value>100</value></property>
	     <property name="initialSize"><value>1</value></property>
	     <property name="maxWait"><value>60000</value></property>
	     <property name="minIdle"><value>1</value></property>
	     <property name="timeBetweenEvictionRunsMillis"><value>60000</value></property>
	     <property name="minEvictableIdleTimeMillis"><value>300000</value></property>
	     <property name="validationQuery"><value>SELECT 'x'</value></property>
	     <property name="testWhileIdle"><value>true</value></property>
	     <property name="testOnBorrow"><value>false</value></property>
	     <property name="testOnReturn"><value>false</value></property>

	     <property name="poolPreparedStatements"><value>true</value></property>
	     <property name="maxOpenPreparedStatements"><value>20</value></property>
	 </bean>

新增一个动态数据源bean

<bean class="com.test.common.dao.DynamicDataSource" id="dataSourceTarget">
	<property name="targetDataSources"> 
	   <map key-type="java.lang.String"> 
	       <entry value-ref="dataSourceOne" key="one"></entry>
	       <entry value-ref="dataSourceTwo" key="two"></entry>
	   </map> 
	</property> 
	<property name="debug"  value="true"/>
	<property name="defaultTargetDataSource" ref="dataSourceOne" ></property>
</bean>

然后是sessionFactory的设置

<bean id="sessionFactory"
		class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
		<property name="dataSource" ref="dataSourceTarget" />
		<property name="namingStrategy">
			<bean class="org.hibernate.cfg.ImprovedNamingStrategy" />
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">${hibernate.dialect}</prop>
				<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
				<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
				<prop key="hibernate.generate_statistics">false</prop>
				<prop key="hibernate.hbm2ddl.auto">update</prop>
			</props>
		</property>
		<property name="packagesToScan">
			<list>
				<value>com.test.**.bean</value>
			</list>
		</property>
	</bean>

可以看到动态数据源使用了重新编写的类,具体代码如下

public class DynamicDataSource extends AbstractRoutingDataSource {
	private boolean debug = false;
	Logger log = Logger.getLogger(DynamicDataSource.class);
	private Map<Object, Object> dynamicTargetDataSources;
	private Object dynamicDefaultTargetDataSource; 
	@Override
	protected Object determineCurrentLookupKey() {
		String datasource = DBContextHolder.getDataSource();
		if (debug) {
			if (StringUtils.isEmpty(datasource)) {
				log.info("---当前数据源:默认数据源---");
			} else {
				log.info("---当前数据源:" + datasource + "---");
			}
		}
		return datasource;
	}

	@Override
	public void setTargetDataSources(Map<Object, Object> targetDataSources) {
		super.setTargetDataSources(targetDataSources);
		this.dynamicTargetDataSources = targetDataSources;
	} 

	// 创建数据源
	public boolean createDataSource(String key, String driveClass, String url, String username, String password) {
		try {
			try { // 排除连接不上的错误
				Class.forName(driveClass);
				DriverManager.getConnection(url, username, password);// 相当于连接数据库
			} catch (Exception e) {
				return false;
			}
			@SuppressWarnings("resource")
			DruidDataSource druidDataSource = new DruidDataSource();
			druidDataSource.setName(key);
			druidDataSource.setDriverClassName(driveClass);
			druidDataSource.setUrl(url);
			druidDataSource.setUsername(username);
			druidDataSource.setPassword(password);
			druidDataSource.setMaxWait(60000);
			druidDataSource.setFilters("stat");
			DataSource createDataSource = (DataSource) druidDataSource;
			druidDataSource.init();
			Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
			dynamicTargetDataSources2.put(key, createDataSource);// 加入map
			setTargetDataSources(dynamicTargetDataSources2);// 将map赋值给父类的TargetDataSources
			super.afterPropertiesSet();// 将TargetDataSources中的连接信息放入resolvedDataSources管理
			return true;
		} catch (Exception e) {
			log.info(e + "");
			return false;
		}
	} 
	// 删除数据源
	public boolean delDatasources(String datasourceid) {
		Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
		if (dynamicTargetDataSources2.containsKey(datasourceid)) {
			Set<DruidDataSource> druidDataSourceInstances = DruidDataSourceStatManager.getDruidDataSourceInstances();
			for (DruidDataSource l : druidDataSourceInstances) {
				if (datasourceid.equals(l.getName())) {
					System.out.println(l);
					dynamicTargetDataSources2.remove(datasourceid);
					DruidDataSourceStatManager.removeDataSource(l);
					setTargetDataSources(dynamicTargetDataSources2);// 将map赋值给父类的TargetDataSources
					super.afterPropertiesSet();// 将TargetDataSources中的连接信息放入resolvedDataSources管理
					return true;
				}
			}
			return false;
		} else {
			return false;
		}
	}

	// 测试数据源连接是否有效
	public boolean testDatasource(String key, String driveClass, String url, String username, String password) {
		try {
			Class.forName(driveClass);
			DriverManager.getConnection(url, username, password);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * Specify the default target DataSource, if any.
	 * <p>
	 * The mapped value can either be a corresponding
	 * {@link javax.sql.DataSource} instance or a data source name String (to be
	 * resolved via a {@link #setDataSourceLookup DataSourceLookup}).
	 * <p>
	 * This DataSource will be used as target if none of the keyed
	 * {@link #setTargetDataSources targetDataSources} match the
	 * {@link #determineCurrentLookupKey()} current lookup key.
	 */
	public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
		super.setDefaultTargetDataSource(defaultTargetDataSource);
		this.dynamicDefaultTargetDataSource = defaultTargetDataSource;
	}


	/**
	 * @param debug
	 *            the debug to set
	 */
	public void setDebug(boolean debug) {
		this.debug = debug;
	}

	/**
	 * @return the debug
	 */
	public boolean isDebug() {
		return debug;
	}

	/**
	 * @return the dynamicTargetDataSources
	 */
	public Map<Object, Object> getDynamicTargetDataSources() {
		return dynamicTargetDataSources;
	}

	/**
	 * @param dynamicTargetDataSources
	 *            the dynamicTargetDataSources to set
	 */
	public void setDynamicTargetDataSources(Map<Object, Object> dynamicTargetDataSources) {
		this.dynamicTargetDataSources = dynamicTargetDataSources;
	}

	/**
	 * @return the dynamicDefaultTargetDataSource
	 */
	public Object getDynamicDefaultTargetDataSource() {
		return dynamicDefaultTargetDataSource;
	}

	/**
	 * @param dynamicDefaultTargetDataSource
	 *            the dynamicDefaultTargetDataSource to set
	 */
	public void setDynamicDefaultTargetDataSource(Object dynamicDefaultTargetDataSource) {
		this.dynamicDefaultTargetDataSource = dynamicDefaultTargetDataSource;
	}
	@Override
	public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException {
		// TODO Auto-generated method stub
		return null;
	}

	

}

这段参考了网上记不清哪个的了,还可以调用代码增加或者删除数据源。然后是切换的数据源的方法类

/**
 * 数据源切换
 * 
 *
 */
public class DBContextHolder {
	 // 对当前线程的操作-线程安全的
	private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

	// 调用此方法,切换数据源
	public static void setDataSource(String dataSource) {  
            contextHolder.set(dataSource);  
    }
	// 获取数据源
	public static String getDataSource() {
	return contextHolder.get();
	}
	// 删除数据源
	public static void clearDataSource() {
		contextHolder.remove();
	}
}
public class DataSourceConst {
	public static final String ONE="one";
	public static final String TWO="two";

}

具体使用DBContextHolder 的setDataSource方法进行切换

DBContextHolder.setDataSource(DataSourceConst.TWO);

注意,切换数据源,只在当前线程生效

自动建表

启动时,因为设置了默认数据源,自动建表会自动为默认数据源建表。可其他数据源就不会自动建表了,这个比较坑,就需要在启动时切换数据源,并运行自动建表的方法,具体如下

@Component("firstSchedulingService")
public class FirstSchedulingService implements ApplicationListener<ContextRefreshedEvent>{
	@Autowired
	private LocalSessionFactoryBean sessionFactory;
	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
		// TODO Auto-generated method stub
		DBContextHolder.setDataSource(DataSourceConst.TWO);
		sessionFactory.updateDatabaseSchema();
	}
}

多数据源的问题基本都解决了,可感觉还是很蛋疼,每次切换只在当前线程生效,这个需要每个操作都切换一次数据源,要不都是操作默认数据。再想想项目的数据真心没有很多,就放弃了。这里记录下,以后万一用的到。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值