java代码里实现动态分表

1.需求:为了实现分表操作,我们所有的数据库创建的时候需要根据operatorId进行分表,所以每次创建一个operator时就需要创建一系列属于该operator的表,就是在表名的后面动态的拼接上operatorId

2.解决办法: 首先,我们需要一个sql模板,模板里面都是建库语句,只是在每个表名的后面加上了一个标志符号#
然后我用operatorId去动态的替换该符号,然后再java代码里执行该sql脚本

测试方法:

  @Value("${properties_name}")
    private  String dbPropertiesName;
 @PostMapping(value = "/test")
    public void test(){
        System.out.println("---------------");
        try {
            replacTextContent("qudao.sql","1");
            run("1");
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    } 
 public static void replacTextContent(String path,String operatorId) throws IOException{
        //原有的内容
        String srcStr = "#";
        //要替换的内容
        String replaceStr = operatorId;
        String newFileName = "F:/qudao_"+operatorId+".sql";
        // 读
        Resource resource = new ClassPathResource(path);
        File sourceFile =  resource.getFile();

        FileReader in = new FileReader(sourceFile);
        BufferedReader bufIn = new BufferedReader(in);
        // 内存流, 作为临时流
        CharArrayWriter tempStream = new CharArrayWriter();
        // 替换
        String line = null;
        while ( (line = bufIn.readLine()) != null) {
            // 替换每行中, 符合条件的字符串
            line = line.replaceAll(srcStr, replaceStr);
            // 将该行写入内存
            tempStream.write(line);
            // 添加换行符
            tempStream.append(System.getProperty("line.separator"));
        }
        // 关闭 输入流
        bufIn.close();
        // 将内存中的流 写入 文件
        FileWriter out = new FileWriter(newFileName);
        tempStream.writeTo(out);
        out.close();
        System.out.println("====path:"+path);
    }
public  void run(String operatorId) {
         try {
                 // 获取数据库相关配置信息
                 Properties props = Resources.getResourceAsProperties(dbPropertiesName);
                 // jdbc 连接信息: 注: 现在版本的JDBC不需要配置driver,因为不需要Class.forName手动加载驱动
                 String url = props.getProperty("spring.datasource.url");
                 String username = props.getProperty("spring.datasource.username");
                 String password = props.getProperty("spring.datasource.password");
                 // 建立连接
                 Connection conn = DriverManager.getConnection(url, username, password);
                 // 创建ScriptRunner,用于执行SQL脚本
                 ScriptRunner runner = new ScriptRunner(conn);
                 runner.setErrorLogWriter(null);
                 runner.setLogWriter(null);

                 // 绝对路径读取
                Reader read = new FileReader(new File("F:/qudao_" + operatorId + ".sql"));
             //相对路径读取
             //Reader read = Resources.getResourceAsReader("F:/qudao_" + operatorId + ".sql");
                 // 执行SQL脚本
                 runner.runScript(read);
                 // 关闭连接
                 conn.close();
                 // 若成功,打印提示信息
                 System.out.println("====== SUCCESS ======");
             } catch (IOException | SQLException e) {
                 e.printStackTrace();
            }
    }

特别注意的是:这里最坑的就是文件位置,我的sql模板是放在resources目录下的,采用上面这种读取文件方式也是因为考虑文件的相对路径而采用的,可能细心的人就发现,为什么我的输出却是写死的绝对路径,而不是相对路径,如果直接输出到resources目录下某个sql文件夹中不是更好嘛,不,我试了很久,都没有成功,于是采用这种绝对地址方式,当然,这是本地测试,如果是服务器上,建议使用OSS

下面是采用oss的方式,说白了就是上面的方法的一种改进,原理是:将文件的模板替换后形成流,直接执行流(就是sql脚本流),然后不用生成文件就直接可以执行,且不会破坏模板,模板也不放在项目中,直接放在阿里云上
下面是代码实现:

 @Autowired
    private ApplicationContext applicationContext ;


    @Value("${ykc.oss.url}")
    private  String ykcOssUrl;

    @Value("${ykc.oss.name}")
    private  String ykcOssName;

    @Value("${ykc.oss.keyId}")
    private  String ykcOssKeyId;

    @Value("${ykc.oss.accessKey}")
    private  String ykcOssAccessKey;

    @Value("${ykc.oss.fileName}")
    private  String ykcOssFileName;```


  public void runSql(String replaceStr) {
        long start = System.currentTimeMillis();
        Connection conn = null;
        try{
            DataSource dataSource =(DataSource) applicationContext.getBean("dataSource");
            conn = (Connection) dataSource.getConnection();
            System.out.print("--------:"+conn);
        }catch (Exception e){

        }

        InputStream is;
        try {
            // 创建OSSClient实例
            OSSClient ossClient = new OSSClient(ykcOssUrl, ykcOssKeyId, ykcOssAccessKey);
            try {
                Date expiration = new Date(System.currentTimeMillis() + 1000 * 1000);
                GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(ykcOssName, ykcOssFileName, HttpMethod.GET);
                // 设置过期时间。
                request.setExpiration(expiration);
                // 生成签名URL(HTTP GET请求)。
                URL signedUrl = ossClient.generatePresignedUrl(request);
                // 使用签名URL发送请求。
                Map<String, String> customHeaders = new HashMap<>(1);
                // 添加GetObject请求头。
                is = ossClient.getObject(signedUrl, customHeaders).getObjectContent();
            } finally {

            }
            //原有的内容
            String srcStr = "10087";
            //要替换的内容
            CharArrayWriter tempStream = new CharArrayWriter();

            // 创建ScriptRunner,用于执行SQL脚本
            ScriptRunner runner = new ScriptRunner(conn);
            runner.setErrorLogWriter(null);
            runner.setLogWriter(null);
            BufferedReader bufferedReader = new BufferedReader(
                    new InputStreamReader(is));
            try {
                // 替换
                String line = null;
                while ((line = bufferedReader.readLine()) != null) {
                    // 替换每行中, 符合条件的字符串
                    line = line.replaceAll(srcStr, replaceStr);
                    // 将该行写入内存
                    tempStream.write(line);
                    // 添加换行符
                    tempStream.append(System.getProperty("line.separator"));
                }
                // 关闭 输入流

            }catch (Exception e){
                log.error("流的替换时出现异常,异常信息为:{}",e);
            }finally {

            }




            char[] s = tempStream.toCharArray();
            CharArrayReader charArrayReader = new CharArrayReader(s);
            Reader read = charArrayReader;
            // 执行SQL脚本
            runner.runScript(read);
            // Reader read = new InputStreamReader(is);
            // 关闭连接
            conn.close();
            // 若成功,打印提示信息
            long end = System.currentTimeMillis();
            long result = end - start;
            log.info("====== SUCCESS ======:"+result);
            ossClient.shutdown();
        } catch ( Exception e) {
            log.error("异常信息为:{}", e);
        }
    }


当然配置文件里得配置相关oss的地址等

#============== oss ===================
ykc.oss.url=https://oss.aliyuncs.com
#内网地址(开发环境)
ykc.oss.name=wtayvyzzmyanm9vx-user-test 
#外网(测试环境等其他环境)
#ykc.oss.name=wtayvyzzmyanm9vx-user-test
ykc.oss.keyId=WTAyVyzZmYaNM9Vx
ykc.oss.accessKey=Us8TMYlwDhPUrAuKh7q3KU5Yqh7hQi
ykc.oss.fileName=qudao.sql

说到这,补充一下,第一种方式里面有一个创建数据库连接,这种方式效率其实比较低效,因为在springboot项目里其实已经连接过了,直接注入
@Autowired
private ApplicationContext applicationContext ;
就可以获取到连接,所以这样写是比较简单的(推荐)




版权声明:本文为weixin_43931650原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。