70

I am starting to use MySQL with JDBC.

Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///x", "x", "x");
stmt = conn.createStatement();
stmt.execute( "CREATE TABLE amigos" +
            "("+
            "id          int AUTO_INCREMENT          not null,"+
            "nombre      char(20)                    not null,"+
            "primary key(id)" +
            ")");

I have 3-4 tables to create and this doesn't look good.

Is there a way to run a .sql script from MySQL JDBC?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Macarse
  • 91,829
  • 44
  • 175
  • 230

13 Answers13

78

Ok. You can use this class here (posted on pastebin because of file length) in your project. But remember to keep the apache license info.

JDBC ScriptRunner

It's ripoff of the iBatis ScriptRunner with dependencies removed.

You can use it like this

Connection con = ....
ScriptRunner runner = new ScriptRunner(con, [booleanAutoCommit], [booleanStopOnerror]);
runner.runScript(new BufferedReader(new FileReader("test.sql")));

That's it!

Benoit Duffez
  • 11,839
  • 12
  • 77
  • 125
jitter
  • 53,475
  • 11
  • 111
  • 124
  • Very handful class. I must add that line 130 can cause headaches. I replaced it to "String trimmedLine = line.trim().replaceAll(";$", Matcher.quoteReplacement("; \\"));" because you might get http://stackoverflow.com/questions/3499483/error-unterminated-quoted-string-at-or-near – gyorgyabraham Jan 10 '13 at 15:48
  • 2
    Can this be used to return a `ResultSet` or it only works for update statements? I tried using it but cannot figure how to get it to return a `ResultSet`. I would go for `Spring` but it is easier to use a class than an entire library. – Alex Burdusel Dec 05 '13 at 14:36
  • be careful with the linked to script a bit--if your script has a code "comment" like `select 1; -- do nothing` then the script doesn't execute it but treats it like the first half of a longer command [concatenated with the new command] other side effect--if that's the last thing in your file, it doesn't run it at all]. If you just stick with single line sql comments it's ok – rogerdpack Oct 03 '14 at 20:42
  • 5
    this class can't handle PROCEDURE ever – Muhamet Aljobairi Jan 14 '15 at 14:31
  • Does not work when creating procedures that utilize DELIMITER and END – shrimpwagon Feb 18 '15 at 18:39
  • 3
    I took the liberty to copy the code, update it to add support for delimiters for stored procedures, upload it to GitHub, and update the link in @jitter's answer. – Benoit Duffez Jul 18 '15 at 19:17
26

I did a lot of research on this and found a good util from spring. I think using SimpleJdbcTestUtils.executeSqlScript(...) is actually the best solution, as it is more maintained and tested.

Edit: SimpleJdbcTestUtils is deprecated. You should use JdbcTestUtils. Updated the link.

Amir Raminfar
  • 33,777
  • 7
  • 93
  • 123
  • 2
    Thanks @Amir Raminfar, your answer helped me out. Anyway just as an update, spring deprecated `SimpleJdbcTestUtil` and advises to use `JdbcTestUtils` in future. – uthomas Jul 14 '13 at 06:38
  • 1
    This is the best answer, the Spring framework team is active. – FBB Nov 07 '13 at 15:38
  • 8
    Starting from Spring 4.0.3. `JdbcTestUtils.executeSqlScript()` methods are now deprecated. `ScriptUtils.executeSqlScript(...)` should be used instead. – Thunder Oct 08 '14 at 10:31
  • 1
    Please note that, actively developed or not, those utility classes were designed with testing in mind, and won't be sufficient for most cases; for example, they won't allow you to create a stored procedure due to delimiter problems; [joe776's answer](http://stackoverflow.com/a/5029925/679240) may be a better option, depending on your need, – Haroldo_OK Jan 21 '16 at 13:31
17

Spring Framework's ResourceDatabasePopulator may help. As you said you're using MySQL and JDBC, let's assume you have a MySQL-backed DataSource instance ready. Further, let's assume your MySQL script files are classpath-locatable. Let's assume you are using WAR layout and the script files are located in a directory src/main/webapp/resources/mysql-scripts/... or src/test/resources/mysql-scripts/.... Then you can use ResourceDatabasePopulator to execute SQL scripts like this:

import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import javax.sql.DataSource;

DataSource dataSource = getYourMySQLDriverBackedDataSource();

ResourceDatabasePopulator rdp = new ResourceDatabasePopulator();    
rdp.addScript(new ClassPathResource(
                        "mysql-scripts/firstScript.sql"));
rdp.addScript(new ClassPathResource(
                        "mysql-scripts/secondScript.sql"));

try {
        Connection connection = dataSource.getConnection();
        rdp.populate(connection); // this starts the script execution, in the order as added
    } catch (SQLException e) {
        e.printStackTrace();
    }
Abdull
  • 26,371
  • 26
  • 130
  • 172
  • 1
    By far the best answer here. I get tired of seeing the answers that tell you to run the MySQL dump import from the command line. Doesn't work so well in an automated environment where the database is on a different server. – Zoidberg Jul 02 '14 at 18:07
  • @Zoidberg The `mysql` command line client and `mysqldump` work fine over a network and can be used in automated scripts. Having the database on a different server should pose no issue. – Asaph Feb 21 '15 at 06:42
  • @Asaph You are correct. I actually tried the above solution and found the performance was really slow. I got it working with the command line argument just fine, so my above comment is actually incorrect. – Zoidberg Mar 08 '15 at 12:22
6

For simple sql script splitted by ';' you can use this simple function. It remove comments and run statements one by one

  static void executeScript(Connection conn, InputStream in)
    throws SQLException
  {
    Scanner s = new Scanner(in);
    s.useDelimiter("/\\*[\\s\\S]*?\\*/|--[^\\r\\n]*|;");

    Statement st = null;

    try
    {
      st = conn.createStatement();

      while (s.hasNext())
      {
        String line = s.next().trim();

        if (!line.isEmpty())
          st.execute(line);
      }
    }
    finally
    {
      if (st != null)
        st.close();
    }
  }
lunicon
  • 1,690
  • 1
  • 15
  • 26
  • 2
    You are assuming that comments can't appear inside of a single statement. In CREATE TABLE this is common. – Eyal Schneider Nov 13 '14 at 10:46
  • I will say even more. It does not support complex queries, but you can change delimeter to something else like ';[\s\r\n]*\\'. and use comments as you like. – lunicon Nov 16 '14 at 12:11
  • sorry but this one work better http://stackoverflow.com/questions/1497569/how-to-execute-sql-script-file-using-jdbc/1498029#1498029 – Atais Oct 06 '15 at 11:53
  • Atais, glad to see your attemps... my code is just an example, use it you wish :) – lunicon Oct 07 '15 at 13:59
5

@Pantelis Sopasakis

Slightly modified version on GitHub: https://gist.github.com/831762/

Its easier to track modifications there.

joe776
  • 1,106
  • 14
  • 23
2

Another interesting option would be to use Jisql to run the scripts. Since the source code is available, it should be possible to embed it into an application.


Edit: took a careful look at it; embedding it inside something else would require some modification to its source code.

Haroldo_OK
  • 6,612
  • 3
  • 43
  • 80
2

Can you use this:

public static void executeSQL(File f, Connection c) throws Exception {
    BufferedReader br = new BufferedReader(new FileReader(f));
    String sql = "", line;
    while ((line = br.readLine()) != null) sql += (line+"\n");
    c.prepareCall(sql).execute(sql);
}

This function gets SQL file and DB connection. Then it reads the file line-by-line using BufferedReader from java.io.
And, finally, executes the read statements.

Java 8+ version:

public static void executeSQL(Path p, Connection c) throws Exception {
    List<String> lines = Files.readAllLines(p);
    String s = String.join("\n", lines.toArray(new String[0]));
    c.prepareCall(s).execute(s);
}
Muskovets
  • 449
  • 8
  • 16
  • While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. Please read this [how-to-answer](http://stackoverflow.com/help/how-to-answer) for providing quality answer. – thewaywewere Jun 07 '17 at 06:23
2

Regarding SQL script runner (which I'm also using), I noticed the following piece of code:

for (int i = 0; i < cols; i++) {
  String value = rs.getString(i);
  print(value + "\t");
}

However, in the API documentation for the method getString(int) it's mentioned that indexes start with 1, so this should become:

for (int i = 1; i <= cols; i++) {
  String value = rs.getString(i);
  print(value + "\t");
}

Second, this implementation of ScriptRunner does not provide support for DELIMITER statements in the SQL script which are important if you need to compile TRIGGERS or PROCEDURES. So I have created this modified version of ScriptRunner: http://pastebin.com/ZrUcDjSx which I hope you'll find useful.

Pantelis Sopasakis
  • 1,902
  • 5
  • 26
  • 45
1

Write code to:

  1. Read in a file containing a number of SQL statements.
  2. Run each SQL statement.
matt b
  • 138,234
  • 66
  • 282
  • 345
  • If I do it like that I should parse the .sql file. I was expecting there was a jdbc's fuction which I couldn't find. – Macarse Jun 25 '09 at 14:37
1

Maven SQL Plugin Use this plugin to execute SQL statements a file or list of files through

  1. sqlCommand
  2. srcFiles 3.fileset configurations
kapil das
  • 2,061
  • 1
  • 28
  • 29
1

For Oracle PL/SQL, the Oracle JDBC-driver indeed supports executing entire SQL-scripts including stored procedures and anonymous blocks (PL/SQL specific notation), see

Can the JDBC Drivers access PL/SQL Stored Procedures?

The Oracle JDBC driver FAQ has more info:

Oracle JDBC drivers support execution of PL/SQL stored procedures and anonymous blocks. They support both SQL92 escape syntax and Oracle PL/SQL block syntax. The following PL/SQL calls would work with any Oracle JDBC driver:

// SQL92 syntax
CallableStatement cs1 = conn.prepareCall
                       ( "{call proc (?,?)}" ) ; // stored proc
CallableStatement cs2 = conn.prepareCall
                       ( "{? = call func (?,?)}" ) ; // stored func
// Oracle PL/SQL block syntax
CallableStatement cs3 = conn.prepareCall
                       ( "begin proc (?,?); end;" ) ; // stored proc
CallableStatement cs4 = conn.prepareCall
                       ( "begin ? := func(?,?); end;" ) ; // stored func

It should be possible to read in a file and feed the content to the prepareCall()-method.

Gregor
  • 1,297
  • 1
  • 19
  • 31
0

There isn't really a way to do this.

You could either run the mysql command line client via Runtime.exec(String[]) and read this article when you decide for this option

Or try using the ScriptRunner (com.ibatis.common.jdbc.ScriptRunner) from ibatis. But it's a bit stupid to include a whole library just to run a script.

jitter
  • 53,475
  • 11
  • 111
  • 124
  • 1
    Yes, it's true. Doesn't make sense to add a lib just to run a script :( I think it's quite strange that jdbc doesn't come with something like that. – Macarse Jun 25 '09 at 14:41
0

Here's a quick and dirty solution that worked for me.

public void executeScript(File scriptFile) {
  Connection connection = null;
  try {
    connection = DriverManager.getConnection(url, user, password);
    if(scriptFile.exists()) {
      var buffer = new StringBuilder();
      var scanner = new Scanner(scriptFile);
      while(scanner.hasNextLine()) {
        var line = scanner.nextLine();
        buffer.append(line);
        // If we encounter a semicolon, then that's a complete statement, so run it.
        if(line.endsWith(";")) {
          String command = buffer.toString();
          connection.createStatement().execute(command);
          buffer = new StringBuilder();
        } else { // Otherwise, just append a newline and keep scanning the file.
          buffer.append("\n");
        }
     }
    }
    else System.err.println("File not found.");
  } catch (SQLException e) {
    e.printStackTrace();
} finally {
  if(connection != null) connection.close();
}
kshep92
  • 841
  • 1
  • 11
  • 22