Java和MySQL控制台REPL

问题描述 投票:2回答:1

我需要编写一个Java代码来创建一个运行MySQL控制台命令的shell,并且可以保留交互式sesion,直到它被故意销毁。

为了模拟它,这是我的代码。

    ProcessBuilder pb;
    pb = new ProcessBuilder(
        "cmd.exe","/c","c://xampp//mysql//bin//mysql","-uroot");

    Process p = pb.start();

    OutputStream stdout = p.getOutputStream();
    InputStream stdin = p.getInputStream();

    BufferedReader reader = new BufferedReader(new InputStreamReader(stdin));
    BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdout));

    boolean written = false;
    while(true) {
        System.out.println("loop... ");

        writer.write("show databases;\r\n\r\n");
        writer.flush();
        writer.newLine();
        writer.newLine();
        writer.flush();
        writer.close(); //If not close, it cannot produce result

        String line;
        line = reader.readLine();
        while (line != null) {
            System.out.println(line);
            line = reader.readLine();
        }
    }

问题是,代码只运行一次,因为在循环中调用了writer.close()。但如果我不打电话给writer.close(),它永远不会回应。

我怎样才能实现目标?

java mysql read-eval-print-loop
1个回答
1
投票

你的问题来自line = reader.readLine()。在找到完整的行之前它不会返回,这由输出流中的换行符表示。但是当你处理mysql的输出时,你不会得到一个完整的行,而是一个“提示”,比如

mysql> _

此外,它将永远不会返回null,直到它正在读取的进程已终止或以其他方式关闭其输出流。因此,您不能将其用作循环终止条件。相反,您应该逐个字符地读取流,查找已提交的命令已完成的指示。

我没有安装mysql,我安装了sqlite3,所以我创建了一个脚本,用一个测试数据库“sql”启动它,查询表t1(单文本列,两行),并打印输出。这重复3次。

ProcessBuilder pb = new ProcessBuilder("/usr/bin/sqlite3", "sql");
Process p = pb.start();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));

// Don't use a BufferedReader; it waits for '\n' characters.
InputStreamReader isr = new InputStreamReader(p.getInputStream());

// Our own "Buffered Reader" workspace 
StringBuilder sb = new StringBuilder();

// Execute 3 times only ...
for(int i=0; i<3; i++) {

   System.out.println("Command: select * from t1;");
   bw.write("select * from t1;\n");
   bw.write(".print !-DONE-!\n");
   bw.flush();

   System.out.println("Result:");
   // Repeat until magic phrase in read buffer
   while (true) {

      // Read characters one at a time...
      char ch = (char) isr.read();

      // Do our own newline processing.
      if (ch == '\n') {
         String line = sb.toString();   // Buffer to string
         sb.setLength(0);               // ... and clear buffer

         if (line.equals("!-DONE-!"))
            break;

         System.out.println(line);
      } else {
         sb.append(ch);
      }
   }
   System.out.println();
}

jshell pb.java的输出:

Command: select * from t1;
Result:
Hello world
Goodbye world!

Command: select * from t1;
Result:
Hello world
Goodbye world!

Command: select * from t1;
Result:
Hello world
Goodbye world!

|  Welcome to JShell -- Version 9
|  For an introduction type: /help intro

jshell> _

似乎stdin来自stdout,另一个进程,我的sqlite3拒绝在sqlite>发出stdout提示。这让我非常悲痛,经过一个小时的各种设置和命令行选项的斗争,我已经放弃了,只是添加了一个打印命令,打印出魔术词!-DONE-!。输出处理查找这个神奇的单词,并突破结果读取循环。

如果你能弄清楚使mysqlmysql> _上吐出stdout提示所需的神奇咒语,那么你的休息条件将在非换行路径中,例如:

      } else {
         sb.append(ch);
         if (sb.length() == 7 && sb.toString().equals("mysql> ")) {
            sb.setLength(0);
            break;
         }
      }
© www.soinside.com 2019 - 2024. All rights reserved.