00.springboot 操作数据库的封装

参考https://gitee.com/owenwangwen/open-capacity-platform项目
springboot springcloud中需要经常使用到关系型数据库或者非关系型数据库,这里做了一个maven的基本模块,别人需要使用关系型或非关系数据库redis时,加入maven依赖即可,代码如下:

    
    
        com.open.capacity
        open-db-core
        ${unieap.platform.version}
    

open-db-core的构思,加入druid数据源操作mysql或者oracle同时支持redis的存储。
pom文件
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0

com.neusoft.unieap
micro-unieap-platform
1.0.0-SNAPSHOT

open-db-core


    
    
        org.springframework.boot
        spring-boot-starter-data-redis
    
    
    
        com.github.ulisesbocchio
        jasypt-spring-boot-starter
        1.8
    

    
    
        org.springframework.boot
        spring-boot-starter-jdbc
    

    
        mysql
        mysql-connector-java
    

    
    
        com.alibaba
        druid
        1.0.31
    
    
    
        com.oracle
        ojdbc6
        11.2.0.3
    

    
    
        org.mybatis.spring.boot
        mybatis-spring-boot-starter
        1.3.0
    

    
    
    
        org.projectlombok
        lombok
        

    
    
        commons-lang
        commons-lang
    
    
        commons-collections
        commons-collections
    
    
        commons-beanutils
        commons-beanutils
    

    
        org.springframework.boot
        spring-boot-starter-web
    

    
        org.springframework
        spring-context-support
    

application.yml文件
server:
port: 7777
tomcat:
uri-encoding: UTF-8
spring:
application:
name: unieap-crontab-core
http:
encoding:
charset: utf8
force: true
enabled: true
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: oracle.jdbc.OracleDriver
url: jdbc:oracle:thin:@127.0.0.1:1521:orcl
username: crm_owner_user
password: bss_crm_test
filters: stat,wall
redis:
database: 1
cluster:
nodes:
130.75.131.237:7000,130.75.131.238:7000,130.75.131.239:7000,130.75.131.237:7001,130.75.131.238:7001,130.75.131.239:7001

timeout: 1000 # 连接超时时间(毫秒)
pool:
  max-active: 10  # 连接池最大连接数(使用负值表示没有限制)
  max-idle: 8     # 连接池中的最大空闲连接
  min-idle: 2     # 连接池中的最小空闲连接
  max-wait: 100   # 连接池最大阻塞等待时间(使用负值表示没有限制)    

mybatis:
config-location: classpath:mybatis.cfg.xml
mapper-locations: classpath*:com/neusoft/*/dao/.xml

logging:
level:
root: INFO
org.hibernate: INFO
org.hibernate.type.descriptor.sql.BasicBinder: TRACE
org.hibernate.type.descriptor.sql.BasicExtractor: TRACE
com.neusoft: DEBUG
com.netflix: DEBUG #用于心跳检测输出的日志

代码文件结构如下

redis代码说明
redis是key-value的存储,为了保证redis的性能,可以将数据分片存储,例如将oauth2的信息分片存储
"access:111111";
"auth_to_access:111111";
"auth:111111";
"refresh_auth:111111";
"access_to_refresh:111111";
"refresh:111111";
"refresh_to_access:111111";
"client_id_to_access:111111";
"uname_to_access:111111";
key都是111111,加前缀分散存储,废话不多说回到redis构建部分,java需要一个key-value序列化的工具 代码如下

package com.neusoft.unieap.redis.config.util;

/**

  • @author 作者 owen E-mail: [email protected]
  • @version 创建时间:2017年7月12日 上午10:50:19
  • 类说明
    */
    import org.springframework.core.convert.converter.Converter;
    import org.springframework.core.serializer.support.DeserializingConverter;
    import org.springframework.core.serializer.support.SerializingConverter;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.data.redis.serializer.SerializationException;

// 此时定义的序列化操作表示可以序列化所有类的对象,当然,这个对象所在的类一定要实现序列化接口
public class RedisObjectSerializer implements RedisSerializer {
// 为了方便进行对象与字节数组的转换,所以应该首先准备出两个转换器
private Converter serializingConverter = new SerializingConverter();
private Converter deserializingConverter = new DeserializingConverter();
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; // 做一个空数组,不是null

@Override
public byte[] serialize(Object obj) throws SerializationException {
    if (obj == null) { // 这个时候没有要序列化的对象出现,所以返回的字节数组应该就是一个空数组
        return EMPTY_BYTE_ARRAY;
    }
    return this.serializingConverter.convert(obj); // 将对象变为字节数组
}

@Override
public Object deserialize(byte[] data) throws SerializationException {
    if (data == null || data.length == 0) { // 此时没有对象的内容信息
        return null;
    }
    return this.deserializingConverter.convert(data);
}

}

序列化工具准备好后,配置redistemplate操作类注入到spring容器中供别人操作使用
package com.neusoft.unieap.redis.config;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import com.neusoft.unieap.redis.config.util.RedisObjectSerializer;

/**

  • @author 作者 owen E-mail: [email protected]
  • @version 创建时间:2017年7月12日 下午3:28:10 类说明br/>*/
    @Configuration
    //没有此属性就不会装配bean
    @ConditionalOnProperty( name="spring.redis.cluster.nodes" , matchIfMissing=false)
    public class RedisConfig {

    @Bean("redisTemplate")br/>@Primary
    public RedisTemplate getRedisTemplate(RedisConnectionFactory factory) {
    RedisTemplate redisTemplate = new RedisTemplate();
    redisTemplate.setConnectionFactory(factory);

    RedisSerializer stringSerializer = new StringRedisSerializer();
    RedisSerializer redisObjectSerializer = new RedisObjectSerializer();
    redisTemplate.setKeySerializer(stringSerializer); // key的序列化类型
    redisTemplate.setHashKeySerializer(stringSerializer);
    redisTemplate.setValueSerializer(redisObjectSerializer); // value的序列化类型
    redisTemplate.afterPropertiesSet(); 
    
    redisTemplate.opsForValue().set("hello", "wolrd");
    
    return redisTemplate;

    }

}

这时,redis的封装已经结束,我们开始下一步关系型数据库的构建,报文采取的是oracle数据库,支持单数据源以及多数据源的方式。
单数据源整合druid的代码如下
package com.neusoft.unieap.db.config;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallFilter;

/**

  • @author 作者 owen E-mail: [email protected]
  • @version 创建时间:2017年11月9日 下午1:47:37 类说明
    */

@Configuration
@ConditionalOnProperty(name = { "spring.datasource.enable.dynamic" }, matchIfMissing = true)
public class DruidConfig {

// 将druid纳入监控步骤如下
// 1通过springboot配置文件注入datasource中
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.alibaba.druid.pool.DruidDataSource", matchIfMissing = false)
public DataSource druidDataSource() {
    return DataSourceBuilder.create().type(DruidDataSource.class).build();
}

// 2.StatViewServlet注入到spring中
// Druid内置提供了一个StatViewServlet用于展示Druid的统计信息。
// 这个StatViewServlet的用途包括:
// 提供监控信息展示的html页面
// 提供监控信息的JSON API
// 注意:使用StatViewServlet,建议使用druid 0.2.6以上版本。
// 注入第三方没有注解的servlet
@Bean
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.alibaba.druid.pool.DruidDataSource", matchIfMissing = false)
public ServletRegistrationBean druidServlet() { // 主要实现WEB监控的配置处理
    ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(),
            "/druid/*"); // 现在要进行druid监控的配置处理操作
    servletRegistrationBean.addInitParameter("allow", "127.0.0.1,130.75.131.208"); // 白名单
    servletRegistrationBean.addInitParameter("deny", "192.168.28.200"); // 黑名单
    servletRegistrationBean.addInitParameter("loginUsername", "owen"); // 用户名
    servletRegistrationBean.addInitParameter("loginPassword", "1q2w3e4r"); // 密码
    servletRegistrationBean.addInitParameter("resetEnable", "false"); // 是否可以重置数据源
    return servletRegistrationBean;
}

// 3.对请求进行过滤
// WebStatFilter注入到spring容器中
// 注入第三方没有注解的过滤器
@Bean
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.alibaba.druid.pool.DruidDataSource", matchIfMissing = false)
public FilterRegistrationBean filterRegistrationBean() {
    FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
    filterRegistrationBean.setFilter(new WebStatFilter());
    filterRegistrationBean.addUrlPatterns("/*"); // 所有请求进行监控处理
    filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.css,/druid/*");
    return filterRegistrationBean;
}

}

此时单库的数据源集成druid mybatis已经集成完毕,下面说明动态数据源的配置。
1.需要继承spring的动态数据源
package com.neusoft.unieap.db.config.dynamic.config.util;

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**

  • @author 作者 owen E-mail: [email protected]
  • @version 创建时间:2017年11月10日 下午3:52:46
  • 类说明
    */
    public class DynamicDataSource extends AbstractRoutingDataSource{

    private Map datasources;

    public DynamicDataSource() {
    datasources = new HashMap<>();

    super.setTargetDataSources(datasources);

    }

    public void addDataSource(DataSourceKey key, T data) {
    datasources.put(key, data);
    }

    @Override
    protected Object determineCurrentLookupKey() {
    return DataSourceHolder.getDataSourceKey();
    }

}

2.定义两个库的标识
package com.neusoft.unieap.db.config.dynamic.config.util;
/**

  • 数据源定义
  • @author owen
  • @create 2017年7月2日
    */
    public enum DataSourceKey {
    crm, bill
    }

3.定义线程变量,供每个调用时分配动态数据源
package com.neusoft.unieap.db.config.dynamic.config.util;

/**

  • 用于数据源切换
  • @author owen
  • @create 2017年7月2日
    */
    public class DataSourceHolder {
    private static final ThreadLocal dataSourceKey = new ThreadLocal<>();

    public static DataSourceKey getDataSourceKey() {
    return dataSourceKey.get();
    }

    public static void setDataSourceKey(DataSourceKey type) {
    dataSourceKey.set(type);
    }

    public static void clearDataSourceKey() {
    dataSourceKey.remove();
    }

}
4.springboot整合动态数据源双库配置
package com.neusoft.unieap.db.config.dynamic.config;

import java.util.ArrayList;
import java.util.List;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallFilter;
import com.neusoft.unieap.db.config.dynamic.config.util.DataSourceKey;
import com.neusoft.unieap.db.config.dynamic.config.util.DynamicDataSource;

/**

  • 定义数据源
  • @author owen
  • @create 2017年7月2日br/>*/
    @Configuration
    @PropertySource("classpath:jdbc.test.properties")
    @ConditionalOnProperty(name = { "spring.datasource.enable.dynamic" }, matchIfMissing = false, havingValue = "true")
    public class DynamicDataSourceConfig {

    private Logger logger = LoggerFactory.getLogger(DynamicDataSourceConfig.class);

    // crm库br/>@Value("${spring.datasource.primary.url:#{null}}")
    private String primaryDbUrl;
    @Value("${spring.datasource.primary.username: #{null}}")
    private String primaryUsername;br/>@Value("${spring.datasource.primary.password:#{null}}")
    private String primaryPassword;

    // bill库br/>@Value("${spring.datasource.secondary.url:#{null}}")
    private String secondaryDbUrl;
    @Value("${spring.datasource.secondary.username: #{null}}")
    private String secondaryUsername;br/>@Value("${spring.datasource.secondary.password:#{null}}")
    private String secondaryPassword;

    // 公共配置br/>@Value("${spring.datasource.driverClassName:#{null}}")
    private String driverClassName;br/>@Value("${spring.datasource.initialSize:#{null}}")
    private Integer initialSize;br/>@Value("${spring.datasource.minIdle:#{null}}")
    private Integer minIdle;br/>@Value("${spring.datasource.maxActive:#{null}}")
    private Integer maxActive;br/>@Value("${spring.datasource.maxWait:#{null}}")
    private Integer maxWait;br/>@Value("${spring.datasource.timeBetweenEvictionRunsMillis:#{null}}")
    private Integer timeBetweenEvictionRunsMillis;br/>@Value("${spring.datasource.minEvictableIdleTimeMillis:#{null}}")
    private Integer minEvictableIdleTimeMillis;br/>@Value("${spring.datasource.validationQuery:#{null}}")
    private String validationQuery;br/>@Value("${spring.datasource.testWhileIdle:#{null}}")
    private Boolean testWhileIdle;br/>@Value("${spring.datasource.testOnBorrow:#{null}}")
    private Boolean testOnBorrow;br/>@Value("${spring.datasource.testOnReturn:#{null}}")
    private Boolean testOnReturn;br/>@Value("${spring.datasource.poolPreparedStatements:#{null}}")
    private Boolean poolPreparedStatements;br/>@Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize:#{null}}")
    private Integer maxPoolPreparedStatementPerConnectionSize;br/>@Value("${spring.datasource.filters:#{null}}")
    private String filters;br/>@Value("{spring.datasource.connectionProperties:#{null}}")
    private String connectionProperties;

    // 不需要纳入spring容器
    public DataSource crmDataSource() {
    DruidDataSource crmDataSource = new DruidDataSource();
    crmDataSource.setUrl(this.primaryDbUrl);
    crmDataSource.setUsername(this.primaryUsername);// 用户名
    crmDataSource.setPassword(this.primaryPassword);// 密码
    crmDataSource.setDriverClassName(driverClassName);
    this.setCommons(crmDataSource);
    return crmDataSource;
    }

    // 不需要纳入spring容器
    public DataSource billDataSource() {
    DruidDataSource billDataSource = new DruidDataSource();
    billDataSource.setUrl(secondaryDbUrl);
    billDataSource.setUsername(secondaryUsername);// 用户名
    billDataSource.setPassword(secondaryPassword);// 密码
    billDataSource.setDriverClassName(driverClassName);

    this.setCommons(billDataSource);
    
    return billDataSource;

    }

    @Bean // 只需要纳入动态数据源到spring容器br/>@Primary
    public DataSource dataSource() {
    DynamicDataSource dataSource = new DynamicDataSource();
    DataSource crmDataSource = this.crmDataSource();
    DataSource billDataSource = this.billDataSource();
    dataSource.addDataSource(DataSourceKey.crm, crmDataSource);
    dataSource.addDataSource(DataSourceKey.bill, billDataSource);
    dataSource.setDefaultTargetDataSource(crmDataSource);

    return dataSource;

    }

    @Bean
    public StatFilter statFilter() {
    StatFilter statFilter = new StatFilter();
    statFilter.setLogSlowSql(true);
    statFilter.setMergeSql(true);
    statFilter.setSlowSqlMillis(1000);

    return statFilter;

    }

    @Bean
    public WallFilter wallFilter() {
    WallFilter wallFilter = new WallFilter();

    // 允许执行多条SQL
    WallConfig config = new WallConfig();
    config.setMultiStatementAllow(true);
    wallFilter.setConfig(config);
    
    return wallFilter;

    }

    @Bean
    public ServletRegistrationBean druidServlet() {
    ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
    servletRegistrationBean.setServlet(new StatViewServlet());
    servletRegistrationBean.addUrlMappings("/druid/*");
    return servletRegistrationBean;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {

    return new JdbcTemplate(dataSource);

    }

    @Bean
    public NamedParameterJdbcTemplate namedParameterJdbcTemplate(DataSource dataSource) {

    return new NamedParameterJdbcTemplate(dataSource);

    }

    private void setCommons(DruidDataSource dataSource) {
    // configuration
    if (initialSize != null) {
    dataSource.setInitialSize(initialSize);
    }
    if (minIdle != null) {
    dataSource.setMinIdle(minIdle);
    }
    if (maxActive != null) {
    dataSource.setMaxActive(maxActive);
    }
    if (maxWait != null) {
    dataSource.setMaxWait(maxWait);
    }
    if (timeBetweenEvictionRunsMillis != null) {
    dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
    }
    if (minEvictableIdleTimeMillis != null) {
    dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
    }
    if (validationQuery != null) {
    dataSource.setValidationQuery(validationQuery);
    }
    if (testWhileIdle != null) {
    dataSource.setTestWhileIdle(testWhileIdle);
    }
    if (testOnBorrow != null) {
    dataSource.setTestOnBorrow(testOnBorrow);
    }
    if (testOnReturn != null) {
    dataSource.setTestOnReturn(testOnReturn);
    }
    if (poolPreparedStatements != null) {
    dataSource.setPoolPreparedStatements(poolPreparedStatements);
    }
    if (maxPoolPreparedStatementPerConnectionSize != null) {
    dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
    }

    if (connectionProperties != null) {
        dataSource.setConnectionProperties(connectionProperties);
    }
    
    List filters = new ArrayList<>();
    filters.add(statFilter());
    filters.add(wallFilter());
    dataSource.setProxyFilters(filters);

    }

    @Bean // 将数据源纳入spring事物管理br/>@Primary
    public DataSourceTransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    public PlatformTransactionManager annotationDrivenTransactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
    }

}

此处一个支持单数据源或者多数据源的关系型数据库核心模块已经集成完毕,支持redis-cluster分布式redis存储的nosql核心模块也已经集成完毕,可以根据业务看是否集成elasticsearch mogondb等存储到此模块,目前我们只使用oracle和redis,只做了以上封装。

转载于:https://blog.51cto.com/13005375/2053873

你可能感兴趣的:(00.springboot 操作数据库的封装)