Jooq配置与使用

1.jooq是一个对象关系映射的轻量级框架,在sql执行方面的灵活性和对数据包装的严谨性方面都很优秀

2.jooq的配置

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.druid.pool.vendor.MySqlValidConnectionChecker;
import java.util.Properties;
import javax.sql.DataSource;
import org.jooq.SQLDialect;
import org.jooq.conf.RenderNameStyle;
import org.jooq.conf.Settings;
import org.jooq.impl.DataSourceConnectionProvider;
import org.jooq.impl.DefaultConfiguration;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;

@Configuration
@ConditionalOnProperty(
    value = {"jooq.enabled"},
    matchIfMissing = true
)
public class JooqConfiguration {
    private static final Logger logger = LogUtil.logger(JooqConfiguration.class);
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.user}")
    private String user;
    @Value("${jdbc.password}")
    private String password;
    @Value("${jdbc.dialect:MYSQL}")
    private String dialect;
    @Autowired
    private Environment env;

    public JooqConfiguration() {
    }

    private String getMaxActive() {
        if (Tool.isNullOrEmpty(K8s.nameSpace())) {
            return this.env.getProperty("jdbc.dev.maxActive", "1");
        } else {
            String str = Env.get("jdbc.maxActive");
            if (Tool.isNullOrEmpty(str)) {
                str = this.env.getProperty("jdbc.maxActive");
            }

            return Tool.isNullOrEmpty(str) ? "40" : str;
        }
    }

    @Bean(
        destroyMethod = "close"
    )
    public DataSource dataSource() throws Exception {
        Properties properties = new Properties();
        properties.put("url", Env.get("jdbc.url", this.url));
        properties.put("driverClassName", Env.get("jdbc.driver", this.driver));
        properties.put("username", Env.get("jdbc.user", this.user));
        properties.put("password", Env.get("jdbc.password", this.password));
        properties.put("initialSize", "1");
        properties.put("maxActive", this.getMaxActive());
        properties.put("maxWait", "10000");
        properties.put("minIdle", "1");
        properties.put("timeBetweenEvictionRunsMillis", "60000");
        properties.put("minEvictableIdleTimeMillis", "300000");
        properties.put("validationQuery", "SELECT 1");
        properties.put("testWhileIdle", "true");
        properties.put("testOnBorrow", "false");
        properties.put("testOnReturn", "false");
        properties.put("maxOpenPreparedStatements", "10");
        properties.put("removeAbandoned", "true");
        properties.put("removeAbandonedTimeout", "1800");
        properties.put("logAbandoned", "true");
        logger.info("data base connection info: " + properties);
        DruidDataSource dds = (DruidDataSource)DruidDataSourceFactory.createDataSource(properties);
        dds.setFilters("log4j");
        properties = new Properties();
        properties.put("druid.stat.slowSqlMillis", "3000");
        properties.put("druid.stat.logSlowSql", "true");
        properties.put("druid.stat.mergeSql", "true");
        dds.setConnectProperties(properties);
        dds.setValidConnectionChecker(new MySqlValidConnectionChecker());
        return dds;
    }

    @Bean
    public DataSourceConnectionProvider connectionProvider(DataSource dataSource) throws Exception {
        return new DataSourceConnectionProvider(new TransactionAwareDataSourceProxy(dataSource));
    }

    @Bean
    public DefaultConfiguration configuration(DataSourceConnectionProvider connectionProvider) throws Exception {
        DefaultConfiguration jooqConfiguration = new DefaultConfiguration();
        jooqConfiguration.set(connectionProvider);
        SQLDialect dia = SQLDialect.valueOf(this.dialect);
        jooqConfiguration.setSQLDialect(dia);
        Settings settings = (new Settings()).withExecuteLogging(true).withRenderFormatted(true);
        if (dia == SQLDialect.H2) {
            settings.withRenderSchema(false).withRenderNameStyle(RenderNameStyle.AS_IS);
        }

        jooqConfiguration.set(settings);
        return jooqConfiguration;
    }
}

3.配置yml

server:
  port: 8085
  servlet:
    context-path: /customer

jdbc:
  url: jdbc:mysql://113.140.12.194:13306/customer_tuiguang?useSSL=false
  datasource: customer_tuiguang
  driver: com.mysql.cj.jdbc.Driver
  user: chdsd
  password: Chdsd20#
  dialect: MYSQL
dev:
  token: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJ0b2dlZWsiLCJuYW1lIjoi5bGx5Lic566h55CG5ZGYIiwiaWQiOiJjaGRzZGFkbWluIn0.FmzO8352eVHKLkNnXx_eT6IgFeZmA2mZ6wxuZaWRxKR3jTT8WUgagjbvVbCcS6O5uAaBFrocXezudr00L6bQmi0KLm3dG7yOrj7RoWpn-dS1o8KaDn-z7MppTBaRBflROD42Mz9XjJNQGWcajrheJsCuAGW8EAgsR3KSrLvqqHDYooLXO8xL7imETx6OcMtv_wHJjauIkWKNyfVsgVzXBuUpZN7ORMZKYEp7DXjGHBPxPCPoDpZaVqYYUjWb98sNmSW2SpgfF3SQMC43W8yVbK8BcFQ9iwHzXiec1Wb0li_U-kJ5wXecDDnARvovVV3nx6Vr5ydtagsq0be-snlcFQ

#service:
#  host: http://113.140.12.194:18083/chd-dev/api
#  url-contract: https://www.chdes.com.cn/chd-dev/api
#  baidu-ak: Y9CacQfm6ZRFfKW7FOjia5lZdyjNvtBa
#dev:
#  token: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJ0b2dlZWsiLCJuYW1lIjoi5bGx5Lic566h55CG5ZGYIiwiaWQiOiJjaGRzZGFkbWluIn0.FmzO8352eVHKLkNnXx_eT6IgFeZmA2mZ6wxuZaWRxKR3jTT8WUgagjbvVbCcS6O5uAaBFrocXezudr00L6bQmi0KLm3dG7yOrj7RoWpn-dS1o8KaDn-z7MppTBaRBflROD42Mz9XjJNQGWcajrheJsCuAGW8EAgsR3KSrLvqqHDYooLXO8xL7imETx6OcMtv_wHJjauIkWKNyfVsgVzXBuUpZN7ORMZKYEp7DXjGHBPxPCPoDpZaVqYYUjWb98sNmSW2SpgfF3SQMC43W8yVbK8BcFQ9iwHzXiec1Wb0li_U-kJ5wXecDDnARvovVV3nx6Vr5ydtagsq0be-snlcFQ

service:
#  host: http://113.140.12.194:18083/chd/api
  activiti:
    host: http://localhost:8080/activiti
  authentication:
    host: http://localhost:8082/authentication
#  host: http://localhost:8080
  baidu-ak: Y9CacQfm6ZRFfKW7FOjia5lZdyjNvtBa

spring:
  servlet:
    multipart:
      max-file-size: 30MB
      max-request-size: 30MB
  liquibase:
    changelog: classpath:/db/changelog/db.changelog-master.xml
  data:
    mongodb:
      host: 39.96.6.37
      port: 27027
      database: dev-attachment
      username: tuji
      password: tuji2018

4.自己封装的Dbvisitor

import lombok.AllArgsConstructor;
import lombok.Data;
import org.jooq.DSLContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;

@Configuration
@Component
public class DBvisitor {

    public static DSLContext dsl;

    public static JdbcTemplate jdbcTemplate;

    @Autowired
    private DSLContext dslContext;

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }

    @Autowired
    public void setTemplate(JdbcTemplate template){
        DBvisitor.jdbcTemplate = template;
    }

    @Autowired
    public void setDsl() {
        DBvisitor.dsl = dslContext;
    }

}

5.gradle配置

buildscript {
    repositories {
        mavenCentral()
        maven {
            url "${nexusUrl}/content/groups/public/"
        }
    }
    dependencies {
        classpath group: 'mysql', name: 'mysql-connector-java', version: '6.0.6'
        classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.0.RELEASE")
        classpath("cn.togeek.plugin:jooq:1.0.0-SNAPSHOT")
        classpath("cn.togeek.plugin:liquibase:1.0.0-SNAPSHOT")
    }
}

apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'cn.togeek.plugin.jooq'
apply plugin: 'cn.togeek.plugin.liquibase'
bootJar {
    baseName = 'customer'
    version =  '2.0.0-SNAPSHOT'
}

repositories {
    mavenCentral()

    maven {
        url "${nexusUrl}/content/groups/public/"
    }
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
//    compile("cn.togeek.power-base:customer-base:chd-tuiguang-SNAPSHOT")
    compile group: 'org.liquibase', name: 'liquibase-core', version: '3.6.1'
    compile project(':customer-base')
    compile "axis:axis:1.4"
    compile "javax.ejb:ejb-api:3.0"
    compile "org.jxls:jxls:2.4.3"
    compile "org.jxls:jxls-jexcel:1.0.6"
    compile "org.jxls:jxls-poi:1.0.14"
    compile group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.0'
    compile group: 'org.freemarker', name: 'freemarker', version: '2.3.23'
    compile group: 'commons-httpclient', name: 'commons-httpclient', version: '3.1'
    compile group: 'org.springframework', name: 'spring-core', version: '5.0.20.RELEASE'
    compile 'cn.hutool:hutool-all:4.0.8'
    compile group: 'org.liquibase', name: 'liquibase-core', version: '3.6.1'
    runtime group: 'mysql', name: 'mysql-connector-java', version: '6.0.6'
    compile 'cn.afterturn:easypoi-base:3.0.3'
    compile 'cn.afterturn:easypoi-web:3.0.3'
    compile 'cn.afterturn:easypoi-annotation:3.0.3'
    compile 'org.apache.pdfbox:pdfbox:2.0.4'
    //compile 'org.apache.poi:ooxml-schemas:1.1'
    compile group: 'com.gexin.platform', name: 'gexin-rp-sdk-http', version: '4.1.0.5'
    compile fileTree(dir: 'libs', include: '*.jar')

    testCompile group: 'com.h2database', name: 'h2', version: '1.4.197'

    compile ('org.apache.poi:poi-ooxml:4.1.2'){
        exclude module: 'xmlbeans'
    }
    compile 'org.apache.xmlbeans:xmlbeans:3.1.0'
}

configurations {
    all*.exclude module: 'spring-boot-starter-logging'
    all*.exclude module: 'logback-core'
    all*.exclude module: 'logback-classic'
}

task buildTask(type: Jar, dependsOn: bootJar) {
    doLast {
        copy {
            from 'k8s'
            into 'build/k8s'
        }
        copy {
            from 'docker'
            into 'build/docker'
        }
        copy {
            from 'build/libs'
            into 'build/docker'
            include '*.jar'
        }
        copy {
            from 'src/main/resources/font'
            into 'build/docker/font'
        }
    }
}

task run(dependsOn: bootJar) << {
    javaexec { main="-jar"; args bootJar.archivePath }
}

compileJava {
    options.encoding = "UTF-8"
}

7.setting.gradle

include ':customer-base'
project(':customer-base').projectDir=file('D:\\ideaWorkspace\\customer-base')

8.定时任务中使用Dbvisitor

import lombok.AllArgsConstructor;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

import static cn.togeek.jooq.Tables.*;

@Configuration
@EnableScheduling
public class TaskPool {

    private final long MILLISECONDS = 1;
    private final long SECONDS = 1000;
    private final long MINUTES = 60 * SECONDS;
    private final long HOURS = 60 * MINUTES;
    private final long DAYS = 24 * HOURS;
    private final Logger logger = LoggerFactory.getLogger(TaskPool.class);
    private String METHOD = "";
    @Autowired
    private DBvisitor DBvisitor;


    @Scheduled(fixedRate= 1 * HOURS)
    private void configureTasks() throws ExecutionException, InterruptedException {
        syncTsorganizationTrorganizationTorganizationIndb();
    }

    /**
     * 客户数据维护
     * 维护auth.torganization,customer.trorganization,customer.tsorganization数据
     * 目标:定期扫描customer.tsorganization数据,维护到auth.torganization和trorganization,customer中
     * 解决:多种客户来源方式,包括但不限于客户导入,客户新增,微信+网厅新增关联企业过程微服务调用链路某个节点异常导致的数据缺失问题
     */
    private void syncTsorganizationTrorganizationTorganizationIndb() throws ExecutionException, InterruptedException {
        List<TSOrganizationRecord> records = DBvisitor.dsl.selectFrom(T_S_ORGANIZATION).where(T_S_ORGANIZATION.TYPE.eq(OrgType.DLYH.getValue())).fetchInto(TSOrganizationRecord.class);
        List<TROrganizationRecord> mappings = DBvisitor.dsl.selectFrom(T_R_ORGANIZATION).fetchInto(TROrganizationRecord.class);
        List<record> customersInAuth = (List<record>) Rest.get(ServiceUrl.AUTH.concat("/organizations/").concat(OrgType.DLYH.getValue()+"/").concat("all"), List.class).get().stream().map(x -> {
            return new record((String)(((Map) x).get("id")),(String)(((Map) x).get("name")));
        }).collect(Collectors.toList());
        List<String> tsIdMappings = mappings.stream().map(TROrganizationRecord::getOrganizationId).distinct().collect(Collectors.toList());
        List<TROrganizationRecord> missingMappings = new CopyOnWriteArrayList<>(); //映射缺省的数据
        List<record> missingTorgnizations = new CopyOnWriteArrayList<>(); //auth库缺省的数据
        List<String> customersName = customersInAuth.stream().map(record::getValue).distinct().collect(Collectors.toList());
        records.stream().forEach(record -> {
            String sId = record.getId();
            String name = record.getName();
            if (!customersName.contains(name)) { //auth 的客户库里没数据
                String uuid = UUIDUtil.getUUID();
                missingTorgnizations.add(new record(sId, uuid));
                if (!tsIdMappings.contains(sId)) { //且customer无映射关系
                    missingMappings.add(buildTrorganization(record, uuid));
                }
            } else { // auth 的客户库里有数据
                if (!tsIdMappings.contains(sId)) { //但customer无映射关系
                    TaskPool.record data = customersInAuth.get(customersName.indexOf(name));
                    missingMappings.add(buildTrorganization(record, data.getKey()));
                }
            }
        });
        String authSql = getSql(records,missingTorgnizations);
        if (!authSql.endsWith("VALUES;")) {
            logger.info("要操作的auth库sql:"+authSql);
            int row = DBvisitor.jdbcTemplate.update(authSql);
            logger.info("auth库插入了{}条数据",row);
        }
        if (!missingMappings.isEmpty()) {
            logger.info("要维护的customer.trorganiztion库的数据个数:"+missingMappings.size());
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("INSERT INTO `customer_tuiguang`.`t_r_organization`(`id`,  `organization_id`) VALUES (");
            missingMappings.stream().forEach(x-> {
                stringBuilder.append("'"+x.getId()+"',").append(""+x.getOrganizationId()+"'),(");
            });
            String mappingSql = stringBuilder.toString();
            mappingSql = mappingSql.substring(0,mappingSql.length()-2);
            int mappingInsert = DBvisitor.jdbcTemplate.update(mappingSql);
//            int length = dsl.batchInsert(missingMappings).execute().length; 插入太耗时
//            logger.info("customer.trorganiztion插入{}条数据",length);
            logger.info("customer.trorganiztion插入{}条数据",mappingInsert);
        }
    }

    private TROrganizationRecord buildTrorganization(TSOrganizationRecord record,String tId) {
        TROrganizationRecord organizationRecord = new TROrganizationRecord();
        BeanUtils.copyProperties(record,organizationRecord);
        organizationRecord.setId(tId);
        organizationRecord.setOrganizationId(record.getId());
        organizationRecord.setPkId(null);
        return organizationRecord;
    }

    private String getSql(List<TSOrganizationRecord> records,List<record> missingTorgnizations){
        StringBuilder sqlbuilder = new StringBuilder();
        /**
         * INSERT INTO `authentication_tuiguang`.`t_organization`(`pk`, `id`, `name`, `alias`, `status`, `type`, `parent_id`, `create_time`, `last_modified_time`, `province_code`, `city_code`, `county_code`, `audit_status`, `applicant`, `audit_reason`, `order`, `power_source`) VALUES (490, '7830A3EC-9610-4260-B29D-35935FA23613-00820', '山东新和成维生素有限公司', NULL, 1, 256, NULL, '2019-01-11 20:16:34', '2019-01-11 20:16:34', NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL);
         */
        List<String> keys = missingTorgnizations.stream().map(record::getKey).collect(Collectors.toList());
        List<String> values = missingTorgnizations.stream().map(record::getValue).collect(Collectors.toList());
        sqlbuilder.append("INSERT INTO `authentication_tuiguang`.`t_organization`(`id`, `name`, `status`, `type`, `create_time`, `last_modified_time`, `order`) VALUES (");
        records.stream().filter(x -> keys.contains(x.getId())).forEach(s -> {
            try {
                sqlbuilder.append(Deal(values.get(keys.indexOf(s.getId())))).append(",")
                        .append(Deal(s.getName())).append(",")
                        .append(1).append(",")
                        .append(OrgType.DLYH.getValue()).append(",")
                        .append(Deal(s.getCreateTime().toString())).append(",")
                        .append(Deal(s.getLastModifiedTime().toString())).append(",")
                        .append(0).append("),(");
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        });
        String sql = sqlbuilder.toString();
        sql = sql.substring(0, sql.length()-2).concat(";");
        return sql;
    }

    private Object Deal(Object obj) throws InstantiationException, IllegalAccessException {
        if (obj == null) {
            if ((obj instanceof String)) {
                return "''";
            } else {
                return null;
            }
        } else {
            if ((obj instanceof String)) {
                return "'"+ obj.toString()  +"'";
            } else {
                return obj.toString();
            }
        }
    }

    @Data
    @AllArgsConstructor
    public class record {
        private String key;
        private String value;
    }
}
原文地址:https://www.cnblogs.com/g177w/p/15588116.html