I have below main method which shows SQL injection flaw (as string concatenation is done here) when scanned for coding standards/rules.
public static void main(String[] args) {
boolean flag = false;
String name = "";
String subName = "abhi";
try {
Class.forName("org.postgresql.Driver");
Connection c = DriverManager.getConnection("url","user", "password");
if(flag==true){
name = "LIKE '%'";
} else {
name = "= LOWER('" + subName + "')";
}
Statement s = c.createStatement();
String query = "SELECT * FROM xyz WHERE name "+name;
ResultSet rs = s.executeQuery(query);
while(rs.next()){
System.out.println(":"+rs.getString(1));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException se) {
se.printStackTrace();
}
}
I want to remove the SQL injection flaw. As my name parameter is dynamic, I cannot set it with preparedStatement. What can be a optimal solution to this?
NOTE: Using 2 different queries in if-else block will not solve the purpose as I have 7 different parameters to be set dynamically which will introduce overhead as there will be many queries.
As my name parameter is dynamic, I cannot set it with preparedStatement.
Sure you can, you just need to treat both the SQL text and the parameters dynamically at the same time.
You should also use try-with-resources, to correctly close the Connection, PreparedStatement, and ResultSet objects.
boolean flag = false;
String subName = "abhi";
try (Connection c = DriverManager.getConnection("url","user", "password")) {
String sql = "SELECT *" +
" FROM xyz" +
" WHERE name " + (flag ? "LIKE '%'"
: "= LOWER(?)");
try (PreparedStatement s = c.prepareStatement(sql)) {
if (! flag)
s.setString(1, subName);
try (ResultSet rs = s.executeQuery()) {
while (rs.next()) {
System.out.println(":"+rs.getString(1));
}
}
}
} catch (SQLException se) {
se.printStackTrace();
}
FYI: WHERE name LIKE '%' is the same as WHERE name IS NOT NULL, which is the same as no WHERE clause if the name column is not nullable.
Related
let say I have simple connection and select from database. Sadness part of this code is that almost let say 40-50% is try-catch boilerplate code. How can I deal with it?
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
try {
connection = DriverManager.getConnection(
"jdbc:mysql://localhost/test",
"root",
"root"
);
statement = connection.createStatement();
String sql = "SELECT id, name from user";
ResultSet rs = statement.executeQuery(sql);
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
System.out.print("ID: " + id);
System.out.print(", name: " + name);
System.out.println();
}
rs.close();
} catch (Exception se) {
se.printStackTrace();
}
finally {
try {
if (statement != null)
statement.close();
} catch (SQLException ignored) {
}
try {
if (connection != null)
connection.close();
} catch (SQLException se) {
se.printStackTrace();
}
}
}
The way to avoid try-catch-finally hell is to use try-with-resources. It was added to the Java language in Java 7. See https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html for a more detailed explanation of this Java language feature.
Here's what your code looks like with try-with-resources
public static void main(String[] args) throws Exception /* bad idea */ {
String sql = "SELECT id, name from user";
try (Connection connection = DriverManager.getConnection(
"jdbc:mysql://localhost/test",
"root",
"root");
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery(sql))
{
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
System.out.print("ID: " + id);
System.out.print(", name: " + name);
System.out.println();
}
}
}
Note that the try-with-resources will automatically close each of the declared resources (connection, statement and rs) in the reverse order that they were declared, and will deal appropriately with any exceptions that may arise during that. So we don't need any explicit close() calls.
If this was production code, we should NOT declare main as throwing exceptions. We should be dealing with the exceptions properly; i.e. logging them and/or producing a user friendly error message. I'm just taking a shortcut here ... because that's a side-bar to your original question.
You can make your code cleaner with try-with-resources, like that-
try(Connection connection = DriverManager.getConnection(
"jdbc:mysql://localhost/test",
"root",
"root"
);Statement statement = connection.createStatement()) {
String sql = "SELECT id, name from user";
ResultSet rs = statement.executeQuery(sql);
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
System.out.print("ID: " + id);
System.out.print(", name: " + name);
System.out.println();
}
rs.close();
} catch (Exception se) {
se.printStackTrace();
}
It'll handle closing resources. For details go to Oracle Docs
DataSource
Set up a DataSource object with all your connection details such as username and password. Your JDBC driver likely comes with a simple implementation.
SomeDataSourceImplementation ds = new SomeDataSourceImplementation() ;
ds.setUserName( "Scott" ) ;
ds.setPassword( "Tiger" ) ;
…
DataSource dataSource = ds ;
Try-with-resources syntax
The try-with-resources syntax was added years ago to help simplify such code. See Tutorial by Oracle.
If an exception is thrown, the AutoCloseable objects are closed in the reverse order in which they were listed.
String sql = "SELECT id, name from user";
try (
Connection conn = dataSource.getConnection() ;
Statement statement = conn.createStatement() ;
ResultSet rs = statement.executeQuery(sql) ;
) {
while ( rs.next() ) {
int id = rs.getInt( "id" ) ;
String name = rs.getString( "name" ) ;
System.out.print( "ID: " + id );
System.out.print( ", name: " + name ) ;
System.out.println();
}
} catch ( SQLException e ) {
e.printStackTrace();
}
I have an SQLite database linked up to my Java project within Eclipse. I'm able to delete entries from the database when I give a hardcoded, specified ID such as '3'. I'm trying to alter the code in order to enable the user the manually pass any number and have it delete that entry.
public static String deleteRecords(String NumberDelete){
Connection dbConnection = null;
Statement statement = null;
try{
dbConnection = getDBConnection();
dbConnection.setAutoCommit(false);
statement = dbConnection.createStatement();
String sql = "DELETE from employees where ID='NumberDelete';";
statement.executeUpdate(sql);
dbConnection.commit();
statement.close();
dbConnection.close();
} catch(Exception e) {
System.err.println(e.getClass().getName() + ": " + e.getMessage());
System.exit(0);
}
return NumberDelete;
}
You need to use PreparedStatement to pass the parameters to query and execute it, the method will look like this:
public static String deleteRecords(String NumberDelete) {
Connection dbConnection = null;
PreparedStatement statement = null;
String sql = "DELETE from employees where ID= ? ;";
try {
dbConnection = getDBConnection();
dbConnection.setAutoCommit(false);
statement = dbConnection.prepareStatement(sql);
statement.setString(1, NumberDelete);
statement.executeUpdate(sql);
dbConnection.commit();
statement.close();
dbConnection.close();
} catch (Exception e) {
System.err.println(e.getClass().getName() + ": " + e.getMessage());
System.exit(0);
}
return NumberDelete;
}
This will set the number in the query and execute it. If the number is of type int then you need to use setInt to set the value. Here is the javadoc for PreparedStatement.
For user input, you might want to check out the Scanner class: https://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html
Once you get an integer from the user, parse it, and store it in a variable, you can simply use String concatenation:
String sql = "DELETE from employees where ID='" + userInput + "';";
I've spent a few hours researching on how to create a Java method that will help with my program. I am very new to MySQL, so I am not the most experienced person out there.
What I am trying to do is write a Java method that checks if a column that has the is named after a username exists inside of a table. If it does not exists, it will create that column.
I have seen lot's of tutorials on the internet about similar solutions to my problem. I don't see how I am supposed to implement the TABLE_SCHEMA into a java method since I only know the very basics of MySql.
It would be nice to see how to implement this or some other solution into the Java method. I've mostly erased my previous work since I could not figure it out, so sorry if I need to show that(This is my first question.)
Edit:
try {
ResultSet res = conn.createStatement()
.executeQuery("ALTER TABLE `package_table` ADD " + username + " VARCHAR(15);");
if (!res.next()) {
conn.createStatement()
.executeUpdate("INSERT INTO `package_table` (`uuid`, `name`, `packages`) VALUE ('"
+ event.getPlayer().getUniqueId() + "', '" + event.getPlayer().getName() + "', '" + "" + "');");
}
} catch (SQLException e) {
e.printStackTrace();
try {
conn.close();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
Edit2:
The reason I need to do columns is that I need to store the 'packages' a username has. There can be "infinite" amounts of packages.
Thanks,
Jack
You can do it using jdbc. Just get column names and then add one if you need.
Something like this:
Connection con;
Statement st;
ResultSet rs;
try {
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/dbname",
"dbuser",
"bdpass"
);
st = con.createStatement();
String sql = "select * from table";
rs = st.executeQuery(sql);
ResultSetMetaData metaData = rs.getMetaData();
int rowCount = metaData.getColumnCount();
boolean isMyColumnPresent = false;
String myColumnName = "myColumnName";
for (int i = 1; i <= rowCount; i++) {
if (myColumnName.equals(metaData.getColumnName(i))) {
isMyColumnPresent = true;
}
}
if (!isMyColumnPresent) {
String myColumnType = "some type";
st.executeUpdate("ALTER TABLE table ADD " + myColumnName + " " + myColumnType);
}
} catch (Exception e) {
e.printStackTrace();
}
You can do something like this,
DatabaseMetaData metadata = conn.getMetaData();
Resultset rs = null;
rs=metadata.getColumns(null, null, "package_table", null);
boolean found=false;
while (rs.next()) {
if(username.equals(rs.getString("COLUMN_NAME"))
{
found=true;
}
}
if(found){
//Skip column creation
}else{
//Create column
}
I have a problem with my program. When I try to write to my database (in MySQL) I get this error "Column count doesn't match value count at row 1"
This is my code:
public void registreerNieuwSpelbord(String spelnaam, String mapcode) {
try (Connection connectie = DriverManager.getConnection(Connectie.JDBC_URL)) {
Statement stmt = connectie.createStatement();
String schrijfSpelbordWeg = "INSERT INTO spelbord(Mapcode, spel_Spelnaam) values('" + mapcode + "," + spelnaam + "')";
stmt.executeUpdate(schrijfSpelbordWeg);
} catch (SQLException ex) {
throw new RuntimeException(ex);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
note: there is also a 3th column with an ID that automatically gives a number
You have two columns listed in the insert, but only one value.
Try this:
String schrijfSpelbordWeg = "INSERT INTO spelbord(Mapcode, spel_Spelnaam) values('" + mapcode + "','" + spelnaam + "')";
You should always use a PreparedStatement and bind variables when dealing with SQL that takes input parameters. This way, you're eliminating the chance of SQL injection, allowing the DB to re-use/cache your query and sparing yourself from hunting down bugs that are caused by missing a quote around a parameter.
Here's a refactored version that uses parameterized SQL:
public void registreerNieuwSpelbord(String spelnaam, String mapcode) {
String sql = "INSERT INTO spelbord(Mapcode, spel_Spelnaam) values(?, ?)";
try (Connection connectie = DriverManager.getConnection(Connectie.JDBC_URL);
PreparedStatement ps = connectie.prepareStatement(sql);) {
ps.setString(1, mapcode);
ps.setString(2, spelnaam);
ps.executeUpdate();
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
}
I am having many rows in table and I ran the same query on my database which is MySql but java ResultSet is only giving the first row of the table. Here is my code.
public ArrayList<String> getAllAlbumsName(Integer uid) {
ArrayList<String>allAlbumsName = new ArrayList<String>();
try {
String qstring = "SELECT albumname FROM picvik_picture_album WHERE " +
"uid = '" + uid + "';";
System.out.println(qstring);
connection = com.picvik.util.MySqlConnection.getInstance().getConnection();
ptmt = connection.prepareStatement(qstring);
resultSet = ptmt.executeQuery();
if(resultSet.next()) {
System.out.println(resultSet.getString("albumname"));
allAlbumsName.add(resultSet.getString("albumname"));
}
resultSet.close();
ptmt.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
return allAlbumsName;
}
if(resultSet.next()) {
System.out.println(resultSet.getString("albumname"));
allAlbumsName.add(resultSet.getString("albumname"));
}
If you would like to get all rows, it should be:
while(resultSet.next()) {
System.out.println(resultSet.getString("albumname"));
allAlbumsName.add(resultSet.getString("albumname"));
}
The while statement continually executes a block of statements while a particular condition is true
Note: As #BalusC commented, your code would introduce SQL Injection attack, it is better to use ptmt.set... Instead of constructing SQL String manually.
try while(resultSet.next()) {
instead of if (resultSet.next()) {
Change if (resultSet.next()) { to while (resultSet.next()) {