Modify array elements

Asked

Viewed 818 times

2

I need to create a query with the insertion of several lines.

  1. Changing an array/list:

arrayOriginal=[10,20,30,40]
valueChave=999

  1. The return must be:

arrayNovo=[(valorChave,10),(valorChave,20),(valorChave,30),(valorChave,40)]

  1. This will complement a query for inserting multiple rows in a table:
INSERT INTO tabela VALUES + '${arrayNovo}';

What I tried but isn’t working:

String[] novosCodigosArray = codigos;

for(int i = 0; i < novosCodigosArray.length; i++)
    novosCodigosArray[i] = "("+chave+","+novosCodigosArray[i]+")";
return Arrays.toString(novosCodigosArray);

// depois utilizo desta maneira para fazer query
String meuINSERT= "INSERT INTO dbo.tabela";
meuINSERT= meuINSERT +" (col_codigo, col_chave)";
meuINSERT= meuINSERT +" VALUES ";
meuINSERT= meuINSERT + novosCodigosArray;

return meuINSERT;

Any help would be most welcome.

  • I won’t be able to answer right now, but this is a serious security problem.

  • Why don’t you mount your query in the loop? And return a string?

  • Something else for each new line in the Insert should have a separate comma.

3 answers

2

First, what to concatenate Strings to create SQL queries this way is very dangerous as it opens your system to a security issue known as injection of SQL.

Doing this is a bad programming practice, because in the case of concatenating Strings, a hacker or malicious user who has control over one of the concatenated values (for example, some form field he filled in), when placing a quote (') and add fragments of SQL in this field, the malicious user will be able to change the structure of his SQL, and may even find a way to put a DROP DATABASE there in the worst case.

To solve this problem (or possible problem) of SQL injection is that Java has the PreparedStatement. Other programming languages also present this concept.

Finally, as you seem to want to make several batch inserts, it is best to use the mechanism of batch. For that, I borrowed the idea of Soen’s reply. That said, then I think this would be your solution:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class MeuDAO {
    private static final String URL = ...;
    private static final String LOGIN = ...;
    private static final String SENHA = ...;

    public MeuDAO() {
    }

    private static final String SQL_INSERT =
            "INSERT INTO tabela (col_codigo, col_chave) VALUES (?, ?)";

    public Connection obterConexao() {
        return DriverManager.getConnection(URL, LOGIN, SENHA);
    }

    public void fazMeuInsert(int chave, int[] valores) {
        if (valores.length == 0) return;
        try (
            Connection conn = obterConexao;
            PreparedStatement ps = conn.prepareStatement(SQL_INSERT))
        ) {
            ps.setInt(1, chave);
            for (int valor : valores) {
                ps.setInt(2, valor);
                ps.addBatch();
            }
            ps.executeBatch();
        } catch (SQLException e) {
            // Tratar o erro adequadamente.
            // Ou então relançar a exceção declarando "throws SQLException".
            // Ou então encapsular em uma outra exceção e lançá-la.
        }
    }
}

Note that once your SQL is a String monolithic, so it is not possible to do SQL injection. Also note that due to the use of the mechanism of Try-with-Resources Java 7 or higher, we don’t need to close the PreparedStatement and the Connection explicitly. In addition, the above code ensures that your SQL only needs to be interpreted once instead of once for each insertion. Also only one connection will open at the beginning and close at the end, instead a lot of connections being opened and closed afterwards. Finally, all of them will be implemented together. All of this guarantees a much better performance and network consumption than it would be to insert them one-by-one.

If your array is very large (type, thousands of elements), maybe you want to divide it into lots so as not to get a batch very large (which can consume a lot of memory both on the client and on the server). Here is the code that automatically divides the array in lots of 1000 elements:

public void fazMeuInsert(int chave, int[] valores) {
    if (valores.length == 0) return;
    try (
        Connection conn = obterConexao;
        PreparedStatement ps = conn.prepareStatement(SQL_INSERT))
    ) {
        int i = 0;
        ps.setInt(1, chave);
        for (int valor : valores) {
            ps.setInt(2, valor);
            ps.addBatch();
            i++;
            if (i % 1000 == 0 || i == valores.size() {
                ps.executeBatch();
            }
        } catch (SQLException e) {
            // Tratar o erro adequadamente.
            // Ou então relançar a exceção declarando "throws SQLException".
            // Ou então encapsular em uma outra exceção e lançá-la.
        }
    }
}
  • I appreciate the answer, however, I cannot use this java structure within the tool I am working on. The SQL connection is performed separately. I must have a script for each item, so I have to find an alternative method. With array is not possible, right?

  • @Fabi published another answer. And yes, with array is possible and very easy to do even. I just don’t know if I understood what you want.

2

Ignoring the security issue and assuming you have Java 8, you can generate SQL tuples in virtually one command:

Arrays.stream(arrayOriginal)
    .map(v -> String.format("(%d,'%s')", chave, v))
    .collect(Collectors.joining(" "));

The first line converts the array into stream. The second maps each value by formatting the tuple as defined. The third line takes the result and joins everything in a string separating the items by a blank.

Obviously you can adjust every step of the process to your needs.

Complete code:

int chave = 999;
String[] arrayOriginal = {"10", "20", "30", "40"};
String resultado = Arrays.stream(arrayOriginal).map(v -> String.format("(%d,'%s')", chave, v)).collect(Collectors.joining(" "));
System.out.println(resultado);

Exit:

(999,'10') (999,'20') (999,'30') (999,'40')

1

Well, whereas you know the dangers of SQL injection and make sure that this is not a risk for you, and that you necessarily need to generate a String large well containing all the Inserts, I would do so:

class Teste {
    private static final String SQL_INSERT =
        "INSERT INTO tabela (col_codigo, col_chave) VALUES ('X', 'Y');\n";

    public static String juntarTodasAsSQLs(String chave, String[] valores) {
        String a = SQL_INSERT.replace("X", chave);
        StringBuilder sb = new StringBuilder(valores.length * (a.length() + 5));
        for (String valor : valores) {
            sb.append(SQL_INSERT.replace("Y", valor));
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        String chave = "999";
        String[] arrayOriginal = {"10", "20", "30", "40"};
        String resultado = juntarTodasAsSQLs(chave, arrayOriginal);
        System.out.println(resultado);
    }
}

Here’s the way out:

INSERT INTO tabela (col_codigo, col_chave) VALUES ('X', '10');
INSERT INTO tabela (col_codigo, col_chave) VALUES ('X', '20');
INSERT INTO tabela (col_codigo, col_chave) VALUES ('X', '30');
INSERT INTO tabela (col_codigo, col_chave) VALUES ('X', '40');

See here this example working on ideone.

But if in your problem, you just need the part that’s after the VALUES and it must necessarily come in the form of a array as a result, so this should help you:

class Teste2 {

    public static String[] juntarTodasAsSQLs(String chave, String[] valores) {
        String[] novoArray = new String[valores.length];
        for (int i = 0; i < valores.length; i++) {
            novoArray[i] = "('" + chave + "', '" + valores[i] + "')";
        }
        return novoArray;
    }

    public static void main(String[] args) {
        String chave = "999";
        String[] arrayOriginal = {"10", "20", "30", "40"};
        String[] resultados = juntarTodasAsSQLs(chave, arrayOriginal);

        for (String r : resultados) {
            System.out.println(r);
        }
    }
}

Here’s the way out:

('999', '10')
('999', '20')
('999', '30')
('999', '40')

See here working on ideone.

  • Hi Victor, I’ll try your second suggestion. I almost succeeded using the following: String[] array = ids; for(int i = 0; i < array.length; i++) array[i] = "("+array[i]+","+codeChave+")"; Return Arrays.toString(array); for example, id is 83 and codeChave170 and output is (8,170), (2,170) ... Thank you so much for the return...

  • if I select 1 id it separates the digits. ---> code used: Key string = 170; String[] arrayOriginal = {82}; String[] results = joinTodasAsSQLs(key, arrayOriginal); String[] jointAsSQLs(Key string, String[] values) { string[] newArray = new String[values.length]; for (int i = 0; i < values.length; i++) { newArray[i] = "('" + key + "', '" + values[i] + "')";} newReturn newArray; } for (String r : results) { Return r; } // returned only ('8', '170')

  • @Could you put that code on Ideone or on your question so I can see how you’re doing? Yeah String chave = 170; does not compile, so I do not know how your code is in fact. Also, seeing source code in comments is very bad as they do not have proper formatting and are very limited in size.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.