11

i have a bunch of sql scripts that should upgrade the database when the java web application starts up.

i tried using the ibatis scriptrunner, but it fails gloriously when defining triggers, where the ";" character does not mark an end of statement.

now i have written my own version of a script runner, which basically does the job, but destroys possible formatting and comments, especially in "create or replace view".

public class ScriptRunner {
private final DataSource ds;


public ScriptRunner(DataSource ds) {
    this.ds = ds;
}

public void run(InputStream sqlStream) throws SQLException, IOException {
    sqlStream.reset();
    final Statement statement = ds.getConnection().createStatement();
    List<String> sqlFragments = createSqlfragments(sqlStream);
    for (String toRun : sqlFragments) {
        if (toRun.length() > 0) {
            statement.execute(toRun);
        }
    }
}

private static List<String> createSqlfragments(InputStream sqlStream) throws IOException {
    BufferedReader br = new BufferedReader(new InputStreamReader(sqlStream));

    List<String> ret = new ArrayList<String>();
    String line;
    StringBuilder script = new StringBuilder();
    while ((line = br.readLine()) != null) {
        if (line.equals("/")) {
            ret.add(removeMultilineComments(script));
            script = new StringBuilder();
        } else {
            //strip comments
            final int indexComment = line.indexOf("--");
            String lineWithoutComments = (indexComment != -1) ? line.substring(0, indexComment) : line;
            script.append(lineWithoutComments).append(" ");
        }
    }
    if (script.length() > 0) {
        ret.add(removeMultilineComments(script));
    }
    return ret;
}

private static String removeMultilineComments(StringBuilder script) {
    return script.toString().replaceAll("/\\*(.*?)\\*/", "").trim();
}

is there a clean way to acieve this? is there something in hibernate i have not seen? or can i pass an inputstream to sqlplus somehow? besides my worries about the formatting, i doubt that this code is error-free, since i have limited knowledge about the pl/sql syntax.

Andreas Petersson
  • 16,248
  • 11
  • 59
  • 91

6 Answers6

7

Use below solution for your reference , i have tried and tested and running successfully.

private static String script_location = "";
private static String file_extension = ".sql";
private static ProcessBuilder processBuilder =null;

public static void main(String[] args) {
    try {
        File file = new File("C:/Script_folder");
        File [] list_files= file.listFiles(new FileFilter() {

            public boolean accept(File f) {
                if (f.getName().toLowerCase().endsWith(file_extension))
                    return true;
                return false;
            }
        });
        for (int i = 0; i<list_files.length;i++){
            script_location = "@" + list_files[i].getAbsolutePath();//ORACLE
            processBuilder = new ProcessBuilder("sqlplus",        "UserName/Password@database_name", script_location); //ORACLE
            //script_location = "-i" + list_files[i].getAbsolutePath();
            //  processBuilder = new ProcessBuilder("sqlplus", "-Udeep-Pdumbhead-Spc-de-deep\\sqlexpress-de_com",script_location);
            processBuilder.redirectErrorStream(true);
            Process process = processBuilder.start();
            BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String currentLine = null;
            while ((currentLine = in.readLine()) != null) {
                System.out.println(" "  + currentLine);
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }catch(Exception ex){
        ex.printStackTrace();
    }
}

Use this snippet code and try and run.

Thanx to user mentioned the solution in the below link:

http://forums.sun.com/thread.jspa?threadID=5413026

Regards | Nitin

Nitin
  • 1
  • 1
  • 1
5

The iBATIS ScriptRunner has a setDelimiter(String, boolean) method. This allows you to have a string other than ";" to be the separator between SQL statements.

In your Oracle SQL script, separate the statements with a "/" (slash).

In your Java code, before calling the runScript do a setDelimter("/", false) which will instruct the ScriptRunner to recognize "/" as statement separator.

  • I am using ibatis-common-2.jar and I haven't found setDelimiter(String, boolean) method, Which version of iBatis you are using? – atish shimpi Nov 03 '14 at 07:29
3

Had the same problem not so long ago, bumped into your question several times while googling for a solution, so I think I owe you—here are my findings so far:

In brief, there are no ready solutions for that: if you open Ant or Maven sources, you'll see they are using a simple regexp-based script splitter which is fine for simple scripts, but usually fails on e.g. stored procedures. Same story with iBATIS, c5 db migrations, etc.

The problem is, there's more than one language involved: in order to run "SQL Scripts" one must be able to handle (1) SQL, (2) PL/SQL, and (3) sqlplus commands.

Running sqlplus itself is the way indeed, but it creates configuration mess, so we tried to avoid this option.

There are ANTLR parsers for PL/SQL, such as Alexandre Porcelli's one—those are very close, but no one prepared a complete drop-in solution based on those so far.

We ended up writing yet another ad hoc splitter which is aware of some sqlplus commands like / and EXIT— it's still ugly, but works for most of our scripts. (Note though some scripts, e.g., with trailing -- comments, won't work—it's still a kludge, not a solution.)

alf
  • 8,377
  • 24
  • 45
2

sqlplus : yes you can. I run sqlplus from within Xemacs(editor) all the time. So, you can run sqlplus in an interpreted mode and then provide it commands and read the output as well.

Another ways is to download the free java based SQL developer tool from oracle (http://www.oracle.com/technology/software/products/sql/index.html). it comes with a sqlcli.bat utility which is a wrapper over a java program. You might want to use this command line utility to do your work.

summary, I would try running sqlplus in the background and provide it's input and reading its output (like emacs does).

anjanb
  • 12,999
  • 18
  • 77
  • 106
0

If you want to write your own script runner, you can use Spring JDBC's SimpleJdbcTemplate (http://static.springframework.org/spring/docs/2.0.x/reference/jdbc.html).

You can, of course, load the scripts as you would any resource in Spring as well.

tunaranch
  • 1,584
  • 1
  • 12
  • 17
0

You can see other people's implementations. See this resource: "Open Source SQL Clients in Java" http://java-source.net/open-source/sql-clients

Dilshod Tadjibaev
  • 1,035
  • 9
  • 18