springboot配置hibernate jpa多数据源
这里我用的springboot项目,配置文件yml文件配置,gradle配置jar包依赖。
找了一天资料,终于整好了多数据源,步骤如下:
application.yml:
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/base?characterEncoding=utf8&useSSL=false #5.5.45+, 5.6.26+ and 5.7.6+版本的mysql需要设置useSSL=false
username: root
password: 123456
maximum-pool-size: 100 #datasource公共配置start
max-idle: 10
max-wait: 10000
min-idle: 5
initial-size: 5
validation-query: SELECT 1
test-on-borrow: false
test-while-idle: true
time-between-eviction-runs-millis: 18800 #datasource公共配置end
jpa:
database: MYSQL
show-sql: true
hibernate:
ddl-auto: update #validate | update | create | create-drop
naming:
strategy: org.hibernate.cfg.DefaultNamingStrategy
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5Dialect #Hibernate方言
freemarker:
allow-request-override: false
allow-session-override: false
cache: false
charset: UTF-8
check-template-location: true
content-type: text/html
enabled: true
expose-request-attributes: false
expose-session-attributes: false
expose-spring-macro-helpers: true
prefer-file-system-access: true
suffix: .html #html静态页面
template-loader-path: classpath:/templates/ #模板路径
settings:
template_update_delay: 0
default_encoding: UTF-8
classic_compatible: true
date_format: yyyy-MM-dd
time_format: HH:mm:ss
datetime_format: yyyy-MM-dd HH:mm:ss
custom:
datasource:
names: ds1 #若需要添加其他数据源,可以直接在此处添加,用逗号隔开例如:(ds2,ds3),相应的下面的数据库配置只需要添加一个配置就可以了
ds1:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test1?characterEncoding=utf8&useSSL=false #5.5.45+, 5.6.26+ and 5.7.6+版本的mysql需要设置useSSL=false
username: root
password: 123456
build.gradle添加相关依赖:
// mysql依赖
compile('mysql:mysql-connector-java')
// druid依赖
compile('com.alibaba:druid:1.0.15')
// jpa依赖
compile('org.springframework.boot:spring-boot-starter-data-jpa')
下面是数据源的配置:
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /**
* 动态数据源
*/
public class DynamicDataSource extends AbstractRoutingDataSource{ /*
* 代码中的determineCurrentLookupKey方法取得一个字符串,
* 该字符串将与配置文件中的相应字符串进行匹配以定位数据源,配置文件,即applicationContext.xml文件中需要要如下代码:(non-Javadoc)
*/
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; /**
* 切换数据源Advice
*/
@Aspect
@Order(-10)//保证该AOP在@Transactional之前执行
@Component
public class DynamicDataSourceAspect { /*
* @Before("@annotation(ds)")
* 的意思是:
*
* @Before:在方法执行之前进行执行:
* @annotation(targetDataSource):
* 会拦截注解targetDataSource的方法,否则不拦截;
*/
@Before("@annotation(targetDataSource)")
public void changeDataSource(JoinPoint point, TargetDataSource targetDataSource) throws Throwable {
//获取当前的指定的数据源;
String dsId = targetDataSource.value();
//如果不在我们注入的所有的数据源范围之内,那么输出警告信息,系统自动使用默认的数据源。
if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {
System.err.println("数据源[{}]不存在,使用默认数据源 > {}"+targetDataSource.value()+point.getSignature());
} else {
System.out.println("Use DataSource : {} > {}"+targetDataSource.value()+point.getSignature());
//找到的话,那么设置到动态数据源上下文中。
DynamicDataSourceContextHolder.setDataSourceType(targetDataSource.value());
}
} @After("@annotation(targetDataSource)")
public void restoreDataSource(JoinPoint point, TargetDataSource targetDataSource) {
System.out.println("Revert DataSource : {} > {}"+targetDataSource.value()+point.getSignature());
//方法执行完毕之后,销毁当前数据源信息,进行垃圾回收。
DynamicDataSourceContextHolder.clearDataSourceType();
} }
import java.util.ArrayList;
import java.util.List; /**
* 动态数据源上下文
*/
public class DynamicDataSourceContextHolder {
/*
* 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); /*
* 管理所有的数据源id;
* 主要是为了判断数据源是否存在;
*/
public static List<String> dataSourceIds = new ArrayList<>(); /**
* 使用setDataSourceType设置当前的
*/
public static void setDataSourceType(String dataSourceType){
contextHolder.set(dataSourceType);
} /**
* 获取当前线程中的数据源
*/
public static String getDataSourceType(){
return contextHolder.get();
} /**
* 删除当前线程池中的数据源
*/
public static void clearDataSourceType(){
contextHolder.remove();
} public static boolean containsDataSource(String dataSourceId){
return dataSourceIds.contains(dataSourceId);
} }
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource; import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata; /**
* 动态数据源注册
*/
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
//如配置文件中未指定数据源类型,使用该默认值
private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";
private ConversionService conversionService = new DefaultConversionService();
private PropertyValues dataSourcePropertyValues; // 默认数据源
private DataSource defaultDataSource; private Map<String, DataSource> customDataSources = new HashMap<String, DataSource>(); /**
* 加载多数据源配置
*/
@Override
public void setEnvironment(Environment environment) {
System.out.println("DynamicDataSourceRegister.setEnvironment()");
initDefaultDataSource(environment);
initCustomDataSources(environment);
} /**
* 加载主数据源配置.
*/
private void initDefaultDataSource(Environment env) {
// 读取主数据源
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource.");
Map<String, Object> dsMap = new HashMap<String, Object>();
dsMap.put("type", propertyResolver.getProperty("type"));
dsMap.put("driverClassName", propertyResolver.getProperty("driverClassName"));
dsMap.put("url", propertyResolver.getProperty("url"));
dsMap.put("username", propertyResolver.getProperty("username"));
dsMap.put("password", propertyResolver.getProperty("password")); //创建数据源;
defaultDataSource = buildDataSource(dsMap);
dataBinder(defaultDataSource, env);
} /**
* 初始化更多数据源
*/
private void initCustomDataSources(Environment env) {
// 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "custom.datasource.");
String dsPrefixs = propertyResolver.getProperty("names");
for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源
Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + ".");
DataSource ds = buildDataSource(dsMap);
customDataSources.put(dsPrefix, ds);
dataBinder(ds, env);
}
} /**
* 创建datasource.
*/
@SuppressWarnings("unchecked")
public DataSource buildDataSource(Map<String, Object> dsMap) {
Object type = dsMap.get("type");
if (type == null) {
type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource
}
Class<? extends DataSource> dataSourceType; try {
dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
String driverClassName = dsMap.get("driverClassName").toString();
String url = dsMap.get("url").toString();
String username = dsMap.get("username").toString();
String password = dsMap.get("password").toString();
DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url).username(username).password(password).type(dataSourceType);
return factory.build();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
} /**
* 为DataSource绑定更多数据
*/
private void dataBinder(DataSource dataSource, Environment env) {
RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
dataBinder.setConversionService(conversionService);
dataBinder.setIgnoreNestedProperties(false);//false
dataBinder.setIgnoreInvalidFields(false);//false
dataBinder.setIgnoreUnknownFields(true);//true if (dataSourcePropertyValues == null) {
Map<String, Object> rpr = new RelaxedPropertyResolver(env, "spring.datasource").getSubProperties(".");
Map<String, Object> values = new HashMap<>(rpr);
// 排除已经设置的属性
values.remove("type");
values.remove("driverClassName");
values.remove("url");
values.remove("username");
values.remove("password");
dataSourcePropertyValues = new MutablePropertyValues(values);
}
dataBinder.bind(dataSourcePropertyValues);
} @Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
System.out.println("DynamicDataSourceRegister.registerBeanDefinitions()");
Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
// 将主数据源添加到更多数据源中
targetDataSources.put("dataSource", defaultDataSource);
DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
// 添加更多数据源
targetDataSources.putAll(customDataSources);
for (String key : customDataSources.keySet()) {
DynamicDataSourceContextHolder.dataSourceIds.add(key);
} // 创建DynamicDataSource
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(DynamicDataSource.class); beanDefinition.setSynthetic(true);
MutablePropertyValues mpv = beanDefinition.getPropertyValues();
//添加属性:AbstractRoutingDataSource.defaultTargetDataSource
mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
mpv.addPropertyValue("targetDataSources", targetDataSources);
registry.registerBeanDefinition("dataSource", beanDefinition);
} }
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* 在方法上使用,用于指定使用哪个数据源
*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String value();
}
测试:
在Controller里:
@Resource
private TestService testService;
@Service
public class TestService { @Resource
private TestDao testDao; /**
* 不指定数据源使用默认数据源
* @return
*/
public List<User> getList(){
return testDao.getList();
} /**
* 指定数据源
* @return
*/
@TargetDataSource("ds1")
public List<User> getListByDs1(){
//return testDao.getListByDs1();
} }
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component; @Component
public class TestDao { @Autowired
private JdbcTemplate jdbcTemplate; /**
* 不指定数据源使用默认数据源
* @return
*/
public List<User> getList(){
String sql = "select * from user";
return (List<User>) jdbcTemplate.query(sql, new RowMapper<User>(){
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));;
return user;
}
});
} /**
* 指定数据源
* 在对应的service进行指定;
* @return
* @author SHANHY
* @create 2016年1月24日
*/
public List<User> getListByDs1(){
/*
* 这张表示复制的主库到ds1的,在ds中并没有此表.
* 需要自己自己进行复制,不然会报错:Table 'test1.User1' doesn't exist
*/
String sql = "select * from user";
return (List<User>) jdbcTemplate.query(sql, new RowMapper<User>(){ @Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));;
return user;
}
});
}
}
至于user表和user对应的实体类,自己创建一个就好。
springboot配置hibernate jpa多数据源的更多相关文章
- Springboot spring data jpa 多数据源的配置01
Springboot spring data jpa 多数据源的配置 (说明:这只是引入了多个数据源,他们各自管理各自的事务,并没有实现统一的事务控制) 例: user数据库 global 数据库 ...
- Springboot配置连接两个数据库
背景: 项目中需要从两个不同的数据库查询数据,之前实现方法是:springboot配置连接一个数据源,另一个使用jdbc代码连接. 为了改进,现在使用SpringBoot配置连接两个数据源 实现效果: ...
- SpringBoot:阿里数据源配置、JPA显示sql语句、格式化JPA查询的sql语句
1 数据源和JPA配置 1.1 显示sql配置和格式化sql配置 者两个配置都是属于hibernate的配置,但是springdatajpa给我们简化了:所有hibernate的配置都在jpa下面的p ...
- springboot之jpa多数据源
1.随着业务复杂程度的增加,我们在单一数据源上面的使用越来越不满足具体的业务逻辑以及实现了. 2.那么多数据源,比如多库多数据库等,我们在使用一个工程的时候多数据源的连接还是很有必要的,这里做一下记录 ...
- SpringBoot之使用jpa/hibernate
Springboot版本是2.1.3.RELEASE 1.依赖 List-1.1 <dependency> <groupId>org.springframework.boot& ...
- springboot集成activiti6.0多数据源的配置
最近公司开始开发springboot的项目,需要对工作流进行集成.目前activiti已经发布了7.0的版本,但是考虑到6.0版本还是比较新而且稳定的,决定还是选择activiti6.0的版本进行集成 ...
- 测试开发专题:spring-boot如何使用JPA进行双向一对多配置
本片文章我们主要介绍spring-boot如何进行JPA的配置以及如何进行实体间的一对多配置. 依赖准备 要在spring-boot使用jpa需要在项目中有进入相关的依赖,pom文件里加入下面内容 & ...
- Spring Boot 2.x 多数据源配置之 JPA 篇
场景假设:现有电商业务,商品和库存分别放在不同的库 配置数据库连接 app: datasource: first: driver-class-name: com.mysql.cj.jdbc.Drive ...
- SpringBoot Jpa 双数据源mysql + oracle + liquibase+参考源码
一.yml文件配置 spring: # 数据库配置 datasource: primary: jdbc-url: jdbc:mysql://localhost:3306/mes-dev?useUnic ...
随机推荐
- free命令(buffer与cache区别/linux查看空闲内存)
自:http://www.cnblogs.com/coldplayerest/archive/2010/02/20/1669949.html Linux上free命令的输出. 下面是free的运行 ...
- APP快速搭建框架
AppDelegate: - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDicti ...
- 微信OAuth2.0网页授权接口
微信OAuth2.0网页授权接口 微信OAuth2.0网页授权接口的thinkphp实现版本号.主要实现了oauth网页受权,以及部分其它接口. 用法 为什么用OAuth2.0受权? 通过OAuth2 ...
- 剖析管理所有大数据组件的可视化利器:Hue
日常的大数据使用都是在服务器命令行中进行的,可视化功能仅仅依靠各个组件自带的web界面来实现,不同组件对应不同的端口号,如:HDFS(50070),Yarn(8088),Hbase(16010)等等, ...
- 20155320 EXP8 Web基础
20155320 EXP8 Web基础 [基础问题回答] 什么是表单? 表单:可以收集用户的信息和反馈意见,是网站管理者与浏览者之间沟通的桥梁. 表单由文本域.复选框.单选框.菜单.文件地址域.按钮等 ...
- MiZ702学习笔记9——XADC采集片上数据PS版
这次借助zynq的内嵌的XADC来采集zynq内部的一些参数: •VCCINT:内部PL核心电压 •VCCAUX:辅助PL电压 •VREFP:XADC正参考电压 •VREFN:XADC负参考电压 •V ...
- C标准库string.h中几个常用函数的使用详解
strlen 计算字符串长度 size_t strlen(const char *str) 计算字符串 str 的长度,直到空结束字符,但不包括空结束字符. 函数实现: int Strlen(cons ...
- Jmeter+ant+Jenkins构建接口自动化测试
1.已写好jmeter脚本 2.安装ant并将ant-jmeter-1.1.1.jar文件放入ant/lib目录,用于调用jmeter 3.修改jmeter的jmeter.properties文件(将 ...
- 小程序swiper组件高度自适应【转载】
最近在做小程序开发,复制官方文档上的swiper组件实测后发现,图片不能自适应.网上找了几个版本测试都发现存在一些小问题,目前这个版本本人实测是最好用的.记录一下,方便日后使用. 感谢原创大神的帮助, ...
- 【大数据实战】将普通文本文件导入ElasticSearch
以<刑法>文本.txt为例. 一.格式化数据 1,首先,ElasticSearch只能接收格式化的数据,所以,我们需要将文本文件转换为格式化的数据---json. 下图为未处理的文本文件. ...