0

I'm trying insert into three tables, using JDBC. My code query rans well in MySQL Workbench, but when I run it using PreparedStatement, I run into error saying

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use

My query looks like

BEGIN; 
INSERT INTO user_authentication (enabled, password, username) values (1,'password','username');
SET @userId = LAST_INSERT_ID();
INSERT INTO account(is_active, balance, user_id) values(1,0.0,@userId); 
INSERT INTO user_information (email, firstname, lastname, user_id) values ('OLeg@mail.com','Oleg','Oleg',@userId); 
INSERT INTO user_authorization (role, user_id) values ('ROLE_USER', @userId); 
COMMIT; 

It seems that error is due to that BEGIN; statement. How can I run multiply insert query using JDBC and PreparedStatement?

Whole method looks like

@Override
public void save(User user) {
    Connection conn = null;
    Account account = user.getAccount();
    UserInformation information = user.getUserInformation();
    Set<UserAuthorization> roles = user.getRoles();
    String insertUserQuery = "INSERT INTO user_authentication (enabled, password, username) values (?,?,?);";
    String insertAccountQuery = "INSERT INTO account(is_active, balance, user_id) values(?,?,@userId);";
    String insertInformationQuery = "INSERT INTO user_information (email, firstname, lastname, user_id) values (?,?,?,@userId);";
    String insertRolesQuery = "INSERT INTO user_authorization (role, user_id) values (?, @userId);";
    String query = "BEGIN;" + insertUserQuery
            + " SET @userId = LAST_INSERT_ID(); " + insertAccountQuery
            + insertInformationQuery + insertRolesQuery + "COMMIT;";
    try {
        conn = dataSource.getConnection();
        PreparedStatement statement = conn.prepareStatement(query);
        statement.setBoolean(1, true);
        statement.setString(2, user.getPassword());
        statement.setString(3, user.getUsername());
        statement.setBoolean(4, true);
        statement.setDouble(5, account.getBalance());
        statement.setString(6, information.getEmail());
        statement.setString(7, information.getFirstName());
        statement.setString(8, information.getLastName());
        for (UserAuthorization role : roles) {
            statement.setString(9, role.getRole().toString());
        }
        statement.executeUpdate();

    } catch (SQLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}
Oleg Kuts
  • 769
  • 1
  • 13
  • 26
  • You can directly do INSERT INTO.... without BEGIN & COMMIT – Yuri Blanc May 19 '16 at 11:49
  • @Yuri, yes, but I need that local variable '@userId', to insert foreign keys. And it seems unnaturally three times trigger database from one java method invocation. – Oleg Kuts May 19 '16 at 11:54
  • You can use Batch inserts to execute multiple queries See This link https://examples.javacodegeeks.com/core-java/sql/jdbc-batch-insert-example/ – Nishant123 May 19 '16 at 11:55
  • @Nishant123, as I understand, batching is for multiply insertion into same table. Also I need that local variable to keep track of foreign key. – Oleg Kuts May 19 '16 at 12:13

1 Answers1

2

You are mixing up terms a bit here. You are trying to execute a transaction, which consists of multiple statements/queries.

You cannot simply concatenate all the strings. You have 4 different inserts. Therefore, you must create 4 (prepared) statement objects in java for these inserts. You will need an additional statement to retrieve the generated id (for the foreign keys).

Regarding BEGIN and COMMIT: You should use the 'commit()' method of the PreparedStatement object. See this Oracle JDBC tutorial

Please also note the line con.setAutoCommit(false); in the example. JDBC has autocommit enabled by default, so you need to turn it off first.

And it seems unnaturally three times trigger database from one java method invocation.

If you run your script from mysql workbench you still execute multiple statements/queries. Your (workbench) sample code 'triggers' the db 7 times, which means there are 7 round-trips workbench <-> database.

yes, but I need that local variable '@userId', to insert foreign keys.

Better to read the generated key back into you program i.e 'select LAST_INSERT_ID()' or better see this question or even better that question. Then use it as a parameter for inserting the other rows.

for (UserAuthorization role : roles) {
    statement.setString(9, role.getRole().toString());
}
statement.executeUpdate();

Won't give you the expected result. You are (re-)setting the parameter over and over again (just in your program, not in the db). See the top section of the tutorial, where the benefits of PreparedStatment are explained.

Hope this clears things up a bit ....

Community
  • 1
  • 1
MartinK
  • 1,938
  • 19
  • 18
  • thanks, clear up my thoughts. I used `PreparedStatement statement = conn.prepareStatement(insertUserQuery,Statement.RETURN_GENERATED_KEYS)` to retrieve foreign key. Also I found that semicolon syntax is not applicable for JDBC statements, so that was the reason for error with `BEGIN;`. – Oleg Kuts May 20 '16 at 08:50