Gestión de fuentes de datos | Verificación dinámica de permisos, estructura de tablas y proceso de migración de datos

Código fuente de este artículo: GitHub · haga clic aquí || GitEE · haga clic aquí

1. Introducción a la sincronización de datos

1. Descripción de la escena

Si a menudo está expuesto al desarrollo de datos, habrá un escenario en el que el Servicio A proporciona una fuente de datos, que se llama fuente de datos dinámica A, y necesita leer los datos debajo de la fuente de datos; El Servicio B proporciona una fuente de datos, que se llama fuente de datos dinámica. B, necesita escribir datos en la fuente de datos. Este escenario generalmente se describe como sincronización de datos o manejo de datos.

2. Proceso básico

Según el diagrama de flujo anterior, los pasos generales son los siguientes:

  • Pruebe si varias fuentes de datos están conectadas con éxito y adminístrelas dinámicamente;
  • Determine si la cuenta proporcionada por la fuente de datos tiene autoridad de operación, como lectura y escritura;
  • Lea la estructura de la tabla de la fuente de datos A, cree una tabla en la fuente de datos B;
  • Lectura de datos o lectura de página, escritura en la fuente de datos B;
  • Sin conocer la estructura de la tabla, también necesita leer la estructura de la tabla y generar SQL;

3. API básica de JDBC

  • Declaración

Una interfaz importante para realizar operaciones de base de datos bajo JDBC en Java, basada en la conexión de base de datos establecida, envía la instrucción SQL para ser ejecutada a la base de datos.

  • Declaración preparada

Herede la interfaz de la Declaración e implemente la compilación previa de SQL para mejorar la eficiencia del procesamiento por lotes. Comúnmente utilizado en escenarios de escritura de datos por lotes.

  • Conjunto resultante

El objeto que almacena el conjunto de resultados de la consulta JDBC. La interfaz ResultSet proporciona un método para recuperar valores de columna de la fila actual.

Segundo, embalaje básico de herramientas.

1. Gestión de la fuente de datos.

Proporcionar una fábrica para la administración de la fuente de datos En el escenario actual, administra principalmente una biblioteca de lectura, una fuente de datos A y una biblioteca de escritura, una fuente de datos B. La conexión de la fuente de datos se verifica y se coloca en un contenedor.

@Component
public class ConnectionFactory {

    private volatile Map<String, Connection> connectionMap = new HashMap<>();

    @Resource
    private JdbcConfig jdbcConfig ;

    @PostConstruct
    public void init (){
        ConnectionEntity read = new ConnectionEntity(
        "MySql","jdbc:mysql://localhost:3306/data_read","user01","123");
        if (jdbcConfig.getConnection(read) != null){
            connectionMap.put(JdbcConstant.READ,jdbcConfig.getConnection(read));
        }
        ConnectionEntity write = new ConnectionEntity(
        "MySql","jdbc:mysql://localhost:3306/data_write","user01","123");
        if (jdbcConfig.getConnection(write) != null){
            connectionMap.put(JdbcConstant.WRITE,jdbcConfig.getConnection(write));
        }
    }

    public Connection getByKey (final String key){
        return connectionMap.get(key) ;
    }
}

2. Costuras dinámicas de SQL

Administración básica de SQL

Principalmente proporciona plantillas básicas de SQL, como búsqueda de tabla completa, búsqueda de paginación y consulta de estructura de tabla.

public class BaseSql {
    public static String READ_SQL = "SELECT * FROM %s LIMIT 1";
    public static String WRITE_SQL = "INSERT INTO %s (SELECT * FROM %s WHERE 1=0)" ;
    public static String CREATE_SQL = "SHOW CREATE TABLE %s" ;
    public static String SELECT_SQL = "SELECT * FROM %s" ;
    public static String COUNT_SQL = "SELECT COUNT(1) countNum FROM %s" ;
    public static String PAGE_SQL = "SELECT * FROM %s LIMIT %s,%s" ;
    public static String STRUCT_SQL (){
        StringBuffer sql = new StringBuffer() ;
        sql.append(" SELECT                     ");
        sql.append("     COLUMN_NAME,           ");
        sql.append("     IS_NULLABLE,           ");
        sql.append("     COLUMN_TYPE,           ");
        sql.append("     COLUMN_KEY,            ");
        sql.append("     COLUMN_COMMENT         ");
        sql.append(" FROM                       ");
        sql.append(" information_schema.COLUMNS ");
        sql.append(" WHERE                      ");
        sql.append(" table_schema = '%s'        ");
        sql.append(" AND table_name = '%s'      ");
        return String.valueOf(sql) ;
    }
}

Costura de parámetros SQL

De acuerdo con los parámetros que faltan en la plantilla SQL, se realiza la finalización dinámica para generar y completar la instrucción SQL.

public class BuildSql {
    /**
     * 读权限SQL
     */
    public static String buildReadSql(String table) {
        String readSql = null ;
        if (StringUtils.isNotEmpty(table)){
            readSql = String.format(BaseSql.READ_SQL, table);
        }
        return readSql;
    }
    /**
     * 读权限SQL
     */
    public static String buildWriteSql(String table){
        String writeSql = null ;
        if (StringUtils.isNotEmpty(table)){
            writeSql = String.format(BaseSql.WRITE_SQL, table,table);
        }
        return writeSql ;
    }
    /**
     * 表创建SQL
     */
    public static String buildStructSql (String table){
        String structSql = null ;
        if (StringUtils.isNotEmpty(table)){
            structSql = String.format(BaseSql.CREATE_SQL, table);
        }
        return structSql ;
    }
    /**
     * 表结构SQL
     */
    public static String buildTableSql (String schema,String table){
        String structSql = null ;
        if (StringUtils.isNotEmpty(table)){
            structSql = String.format(BaseSql.STRUCT_SQL(), schema,table);
        }
        return structSql ;
    }
    /**
     * 全表查询SQL
     */
    public static String buildSelectSql (String table){
        String selectSql = null ;
        if (StringUtils.isNotEmpty(table)){
            selectSql = String.format(BaseSql.SELECT_SQL,table);
        }
        return selectSql ;
    }
    /**
     * 总数查询SQL
     */
    public static String buildCountSql (String table){
        String countSql = null ;
        if (StringUtils.isNotEmpty(table)){
            countSql = String.format(BaseSql.COUNT_SQL,table);
        }
        return countSql ;
    }
    /**
     * 分页查询SQL
     */
    public static String buildPageSql (String table,int offset,int size){
        String pageSql = null ;
        if (StringUtils.isNotEmpty(table)){
            pageSql = String.format(BaseSql.PAGE_SQL,table,offset,size);
        }
        return pageSql ;
    }
}

3. Proceso de negocio

1. Autenticación básica

La lectura de la biblioteca intenta una sola lectura de datos, y la escritura de la biblioteca intenta una escritura incondicional.Si no hay permiso, se lanzará una excepción correspondiente.

@RestController
public class CheckController {
    @Resource
    private ConnectionFactory connectionFactory ;
    // MySQLSyntaxErrorException: SELECT command denied to user
    @GetMapping("/checkRead")
    public String checkRead (){
        try {
            String sql = BuildSql.buildReadSql("rw_read") ;
            ExecuteSqlUtil.query(connectionFactory.getByKey(JdbcConstant.READ),sql) ;
            return "success" ;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return "fail" ;
    }
    // MySQLSyntaxErrorException: INSERT command denied to user
    @GetMapping("/checkWrite")
    public String checkWrite (){
        try {
            String sql = BuildSql.buildWriteSql("rw_read") ;
            ExecuteSqlUtil.update(connectionFactory.getByKey(JdbcConstant.WRITE),sql) ;
            return "success" ;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return "fail" ;
    }
}

2. Estructura de la tabla de sincronización

Aquí, se realiza la operación más simple, y la instrucción de consulta para leer la tabla de la biblioteca se consulta y se arroja a la biblioteca de escritura para su ejecución.

@RestController
public class StructController {
    @Resource
    private ConnectionFactory connectionFactory ;
    @GetMapping("/syncStruct")
    public String syncStruct (){
        try {
            String sql = BuildSql.buildStructSql("rw_read") ;
            ResultSet resultSet = ExecuteSqlUtil.query(connectionFactory.getByKey(JdbcConstant.READ),sql) ;
            String createTableSql = null ;
            while (resultSet.next()){
                createTableSql = resultSet.getString("Create Table") ;
            }
            if (StringUtils.isNotEmpty(createTableSql)){
                ExecuteSqlUtil.update(connectionFactory.getByKey(JdbcConstant.WRITE),createTableSql) ;
            }
            return "success" ;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return "fail" ;
    }
}

3. Sincronizar datos de la tabla

Los datos de la tabla de la biblioteca de lectura se leen y se colocan en la biblioteca de escritura en lotes. En particular, aquí hay un método: Statement.setObject (); cuando no se conoce el número y el tipo de parámetros, el tipo de datos se adapta automáticamente.

@RestController
public class DataSyncController {
    @Resource
    private ConnectionFactory connectionFactory ;
    @GetMapping("/dataSync")
    public List<RwReadEntity> dataSync (){
        List<RwReadEntity> rwReadEntities = new ArrayList<>() ;
        try {
            Connection readConnection = connectionFactory.getByKey(JdbcConstant.READ) ;
            String sql = BuildSql.buildSelectSql("rw_read") ;
            ResultSet resultSet = ExecuteSqlUtil.query(readConnection,sql) ;
            while (resultSet.next()){
                RwReadEntity rwReadEntity = new RwReadEntity() ;
                rwReadEntity.setId(resultSet.getInt("id"));
                rwReadEntity.setSign(resultSet.getString("sign"));
                rwReadEntities.add(rwReadEntity) ;
            }
            if (rwReadEntities.size() > 0){
                Connection writeConnection = connectionFactory.getByKey(JdbcConstant.WRITE) ;
                writeConnection.setAutoCommit(false);
                PreparedStatement statement = writeConnection.prepareStatement("INSERT INTO rw_read VALUES(?,?)");
                // 基于动态获取列,和statement.setObject();自动适配数据类型
                for (int i = 0 ; i < rwReadEntities.size() ; i++){
                    RwReadEntity rwReadEntity = rwReadEntities.get(i) ;
                    statement.setInt(1,rwReadEntity.getId()) ;
                    statement.setString(2,rwReadEntity.getSign()) ;
                    statement.addBatch();
                    if (i>0 && i%2==0){
                        statement.executeBatch() ;
                    }
                }
                // 处理最后一批数据
                statement.executeBatch();
                writeConnection.commit();
            }
            return rwReadEntities ;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null ;
    }
}

4. Consulta de paginación

Proporcione una herramienta de consulta de paginación, que no puede leer una gran cantidad de datos a la vez cuando el volumen de datos es grande, para evitar el consumo excesivo de recursos.

public class PageUtilEntity {
    /**
     * 分页生成方法
     */
    public static PageHelperEntity<Object> pageResult (int total, int pageSize,int currentPage, List dataList){
        PageHelperEntity<Object> pageBean = new PageHelperEntity<Object>();
        // 总页数
        int totalPage = PageHelperEntity.countTotalPage(pageSize,total) ;
        // 分页列表
        List<Integer> pageList = PageHelperEntity.pageList(currentPage,pageSize,total) ;
        // 上一页
        int prevPage = 0 ;
        if (currentPage==1){
            prevPage = currentPage ;
        } else if (currentPage>1&&currentPage<=totalPage){
            prevPage = currentPage -1 ;
        }
        // 下一页
        int nextPage =0 ;
        if (totalPage==1){
            nextPage = currentPage ;
        } else if (currentPage<=totalPage-1){
            nextPage = currentPage+1 ;
        }
        pageBean.setDataList(dataList);
        pageBean.setTotal(total);
        pageBean.setPageSize(pageSize);
        pageBean.setCurrentPage(currentPage);
        pageBean.setTotalPage(totalPage);
        pageBean.setPageList(pageList);
        pageBean.setPrevPage(prevPage);
        pageBean.setNextPage(nextPage);
        pageBean.initjudge();
        return  pageBean ;
    }
}

Cuarto, el resumen final

Muchas empresas de alta complejidad deben resolverse con la ayuda de API básicas. Debido a la alta complejidad, no es fácil abstraer y encapsular paquetes unificados. Si la sincronización de datos es un negocio, puede adaptarse a múltiples bases de datos y puede encapsularse de forma independiente como middleware También hay una gran cantidad de middleware sobre sincronización o cálculo de datos de múltiples partes en proyectos de código abierto. Puede comprenderlo usted mismo y ampliar sus horizontes.

Cinco, dirección de código fuente

GitHub·地址
https://github.com/cicadasmile/data-manage-parent
GitEE·地址
https://gitee.com/cicadasmile/data-manage-parent

Recomendar lectura relacionada
Gestión de la fuente de datos: enrutamiento dinámico de la biblioteca maestro-esclavo, separación de lectura-escritura en modo AOP
Gestión de fuentes de datos: según el modo JDBC, adapte y gestione fuentes de datos dinámicas

Supongo que te gusta

Origin www.cnblogs.com/cicada-smile/p/12694776.html
Recomendado
Clasificación