spring学习的仓库: git@gitee.com:jackyqidldd/spring.git
- 先来一个入门的例子,不用任何配置,方便理解
-
DataSourceProperties.java 数据源配置的JavaBean类
@Data @Validated public class DataSourceProperties extends AbstractMappedProperties{ @NotBlank private String url; @NotBlank private String password; @NotBlank private String username; /** * query timeout in seconds */ @Min(0) private int timeout=30; private String driverClassName; Map<String,Object> properties; /** * 其他配置项,例如setting.maxWait,setting.minIdle等(不同的数据源配置不一样) * 对于druid,可配置项请参考{@link com.alibaba.druid.pool.DruidDataSource} */ private Map<String,Object> setting=new HashMap<>(); }
-
DefaultDataSource 数据源,用到阿里的DruidDataSource
public class DefaultDataSource extends DruidDataSource{ private DataSourceProperties dataSourceProperties; private Map<String,String> driverMap; private String dbType; public DefaultDataSource(DataSourceProperties dataSourceProperties) { this.dataSourceProperties = dataSourceProperties; String url=dataSourceProperties.getUrl(); this.dbType=getDbType(url); this.driverMap=getDriverMap(); //1,从dataSourceProperties配置读取 setUrl(url); setDriverClassName(getDriverClassNameByUrl()); setPassword(dataSourceProperties.getPassword()); setUsername(dataSourceProperties.getUsername()); setQueryTimeout(dataSourceProperties.getTimeout()); //2. 从dataSourceProperties.setting配置读取 initDataSourceReflectively(dataSourceProperties.getSetting()); }
- 动态数据源
public class DynamicDataSource extends AbstractRoutingDataSource { public DynamicDataSource() { setTargetDataSources(TargetDataSourceHolder.get()); } @Override public void setTargetDataSources(Map targetDataSources) { super.setTargetDataSources(targetDataSources); } @Override public Object unwrap(Class iface) throws SQLException { return null; } @Override public boolean isWrapperFor(Class iface) throws SQLException { return false; } //这里为了方便演示,直接写死的数据源名字,实际应用中是当前的数据源 @Override protected Object determineCurrentLookupKey() { String dataSourceName = "default"; return dataSourceName; } @Override public void afterPropertiesSet() { super.afterPropertiesSet(); Map<Object, Object> targetDataSources = ReflectionUtils.getFieldValue(this, "targetDataSources"); ReflectionUtils.setFieldValue(this, "resolvedDataSources", targetDataSources); }
-
DynamicDataSource 注册到容器
@Configuration public class DbConfiguration { @Bean @ConditionalOnMissingBean public DynamicDataSource dynamicDataSource() { DynamicDataSource dataSource = new DynamicDataSource(); return dataSource; } }
- 至此准备完成,开始写测试方法
@RunWith(SpringJUnit4ClassRunner.class) @ComponentScan(value = {"com.example.spring"}) @EnableAutoConfiguration public class TestMain { @Autowired DataSource dataSource; @Test public void testDb() { NamedParameterJdbcTemplate namedParameterJdbcTemplate; DataSourceProperties dataSourceProperties = new DataSourceProperties(); dataSourceProperties.setDriverClassName("com.mysql.jdbc.Driver"); dataSourceProperties.setPassword("1111"); dataSourceProperties.setUrl("jdbc:mysql://10.11.11.1:3306/point"); dataSourceProperties.setUsername("root"); Map<String, DataSource> map = new HashMap<>(); map.put("default", new DefaultDataSource(dataSourceProperties)); TargetDataSourceHolder.put("default", new DefaultDataSource(dataSourceProperties)); MapSqlParameterSource mapSqlParameterSource = new MapSqlParameterSource(); mapSqlParameterSource.addValue("id", "50"); namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); List<Map<String, Object>> result = namedParameterJdbcTemplate.query("select * from pk_config where step=:id", mapSqlParameterSource, new ColumnMapRowMapper()); System.out.println("******************************"); System.out.println(JSONUtils.toJSONString(result)); System.out.println("******************************");
执行结果:
一个简单的jdbc操作就完成了,但是还有很多需要改进的地方
- db的配置冗余在代码里,可以放到配置文件里,格式如下
default: url: jdbc:mysql://10.10.10.10:3306/xx username: root password: 111 db1: url: jdbc:mysql://10.10.10.10:3306/xxx username: root password: 222
- 动态数据源DynamicDataSource#determineCurrentLookupKey 写死了数据源"default",改进如下
- 初始化把所有数据源保存到DataSourceHolder,类型为:Map<String, DataSource>
- 用户指定了数据源,如:
//动态设置数据源 @DataSourceKey("db1")
则同样把name放到一个DataSourceHolder,类型为ThreadLocal<String>
ThreadLocal<String> holder = new ThreadLocal<String>();
所以determineCurrentLookupKey方法可以写成:
@Override protected Object determineCurrentLookupKey() { String dataSourceName = DynamicDataSourceHolder.getDataSourceName(); return dataSourceName; }
还有可以扩展的地方:
namedParameterJdbcTemplate.query(String sql, SqlParameterSource paramSource, RowMapper<T> rowMapper)SqlParameterSource sql里的参数传参的方法有2个实现类:
- BeanPropertySqlParameterSource 直接把bean转成输入参数
MapSqlParameterSource 输入参数为map,sql中可以用:对象.变量 的格式
RowMapper db数据和Javabean的映射,也有2个实现类:
ColumnMapRowMapper BeanPropertyRowMapper 将数据库查询结果转换为Java类对象。 常应用于使用Spring的JdbcTemplate查询数据库,获取List结果列表,数据库表字段和实体类自动对应。