Attempting to enter values to database tables using jdbc transaction - java

I'm attempting to enter a set of values to tables reservation,resdetails using database transactions. There are no exceptions thrown, but values are not being entered in to DB.Here i've used utilised java.sql.PreparedStatement.
public boolean addReservation(Reservation res, ArrayList<ReservationDetails> resdetlist) throws Exception {
connection = DBConnection.getDBConnection();
try {
System.out.println("A");
connection.setAutoCommit(false);
PreparedStatement ps1 = connection.prepareStatement("insert into reservation values(?,?,?,?)");
ps1.setString(1, res.getResid());
ps1.setBoolean(2, res.isCheckin_status());
ps1.setString(3, res.getRes_from());
ps1.setString(4, res.getRes_till());
int addedres = ps1.executeUpdate();
System.out.println("addres:" + addedres);
System.out.println("B");
if (addedres > 0) {
for (ReservationDetails resdet : resdetlist) {
//int addedresdet = addReservationDetails(resdet);
PreparedStatement ps2 = connection.prepareStatement("insert into resdetails values(?,?,?,?,?,?)");
ps2.setString(1, resdet.getResid());
ps2.setString(2, resdet.getNic());
ps2.setString(3, resdet.getPayment_id());
ps2.setString(4, resdet.getRoom_no());
ps2.setString(5, resdet.getType_of_accomodation());
ps2.setString(6, resdet.getDate_of_reservation());
int addedresdet = ps2.executeUpdate();
System.out.println("addedresdet:" + addedresdet);
System.out.println("C");
if (addedresdet <= 0) {
System.out.println("D");
connection.rollback();
return false;
}
}
} else {
System.out.println("E");
connection.rollback();
return false;
}
connection.commit();
} catch (Exception ex) {
// ex.printStackTrace();
connection.rollback();
} finally {
connection.setAutoCommit(true);
System.out.println("F");
}
return true;
}

If executeUpdate() returns <= 0 then nothing changed in the database, therefore there's no need to rollback the transaction. Also, if a problem occurred while inserting the entry (eg. a constraint violation, etc) then the executeUpdate() throws a SQLException. I would change you code to:
try {
connection.setAutoCommit(false);
PreparedStatement ps1 = ...
...
ps1.executeUpdate();
for (ReservationDetails resdet : resdetlist) {
PreparedStatement ps2 = ...
...
ps2.executeUpdate();
}
connection.commit();
} catch (SQLException e) {
connection.rollback();
}
Also, don't forget to call close() on the PreparedStatements after you use them, and on the connection object at the end of the method.

Related

How can I not close the connection due to a transaction if I use try-with-resources?

I have a DAO which has method to insert entities into a MySQL database. That method takes a connection and entity as parameters. In Context.xml file, I set that connection will have defaultAutoCommit="false" property, so I don't need to set it inside DAO methods.
defaultAutoCommit="false"
#Override
public boolean insertCarCategory(Connection connection, CarCategory carCategory) {
int rowNum = 0;
String query = "INSERT INTO car_category values(?,?,?,?);";
try (Connection con = connection;
AutoRollback autoRollback = new AutoRollback(con);
PreparedStatement statement = con.prepareStatement(query)) {
statement.setString(1, carCategory.getCarCategory());
statement.setDouble(2, carCategory.getCostPerOneKilometer());
statement.setDouble(3, carCategory.getDiscount());
statement.setBytes(4, ImageUtil.imageToByte(carCategory.getCarCategoryImage()));
rowNum = statement.executeUpdate();
//if it used as transaction dont commit and close connection
autoRollback.commit();
} catch (SQLException e) {
LOGGER.error(e);
}
return rowNum > 0;
}
UserDao method that will be used In Service Layer
#Override
public boolean insertUser(Connection connection,User user) {
int rowNum = 0;
String query = "INSERT INTO user_info(login,userPassword,userType,userEmail)values(?,?,?,?);";
ResultSet keys = null;
try(Connection con = connection;
AutoRollback autoRollback = new AutoRollback(con);
PreparedStatement statement = con.prepareStatement(query,Statement.RETURN_GENERATED_KEYS)) {
statement.setString(1, user.getLogin());
statement.setString(2, PasswordUtil.generateStrongPasswordHash(user.getPassword()));
statement.setString(3, user.getUserType());
statement.setString(4, user.getUserEmail());
rowNum = statement.executeUpdate();
keys = statement.getGeneratedKeys();
if (keys.next()) {
user.setUserId(keys.getInt(1));
}
autoRollback.commit();
} catch (SQLException e) {
LOGGER.error(e);
} finally {
if (keys != null) {
try {
keys.close();
} catch (SQLException e) {
LOGGER.error(e);
}
}
}
return rowNum > 0;
}
I use AutoRollBack class that helps me to rollback transaction If commit is false
public class AutoRollback implements AutoCloseable {
private Connection conn;
private boolean committed;
public AutoRollback(Connection conn) throws SQLException {
this.conn = conn;
}
public void commit() throws SQLException {
conn.commit();
committed = true;
}
#Override
public void close() throws SQLException {
if(!committed) {
conn.rollback();
}
}
}
In the service layer, I use DAO methods. I get a connection from a connection pool and pass it to DAO methods.
private void insertCarUser(User user,CarCategory carCategory){
Connection connection = MySQLDAOFactory.getConnection();
categoryDao.insertCarCategory(connection,carCategory);
userDao.insertUser(connection,user);
}
How can I not close connection in one of the methods so that it can be used in the second?
Remove the try-with-resources in the various DAO methods, and instead apply try-with-resource immediately when obtaining a connection:
private void insertCarUser(User user,CarCategory carCategory){
try (Connection connection = MySQLDAOFactory.getConnection()) {
categoryDao.insertCarCategory(connection,carCategory);
userDao.insertUser(connection,user);
}
}
Similarly, you will want to move you transaction handling there, and not in your DAO methods if this operation needs to be atomic.

try-with-resources JDBC with multiple resources and an else statement

This is a method to add a new planet to an observablelist of customers.
I am wondering if I am using the try with resources correctly and if the auto-close is working.
public static Customer addPlanet(Customer customer) {
String query1 = "Select * from planet where planet=? AND universeID=?";
String query2 = "INSERT INTO planet (planet,universeID) VALUES(?,?)";
try (PreparedStatement statement = (PreparedStatement) Database.connection.prepareStatement(query1);
PreparedStatement statement2 = (PreparedStatement) Database.connection.prepareStatement(query2)) {
statement.setString(1, customer.getPlanet());
statement.setString(2, Integer.toString(customer.getUniverseID()));
try (ResultSet rs = statement.executeQuery()) {
if (rs.next()) {
int planetId = rs.getInt(1);
customer.setPlanetID(planetId);
return customer;
} else {
statement2.setString(1, customer.getPlanet());
statement2.setInt(2, customer.getUniverseID());
statement2.executeUpdate();
return addPlanet(customer);
}
} catch (SQLException e) {
e.printStackTrace();
return null;
}
} catch (SQLException e) {
e.printStackTrace();
}
return customer;
}
My question is, does this part need to be enclosed in a try-catch block or does it get closed automatically.
statement2.executeUpdate();
It gets closed. Anything in the try gets closed at the end if they are AutoCloseable.

how to set local variable for transaction in jdbc

I'm working on a project in whose database I need to initialize a session variable. If I work directly with sql, the initialization is done with SET statement
set local app.user_id to "0000";
I try to initialize it with Connection#setClientInfo() but failed
try(Connection connection = getDataSource().getConnection()) {
boolean isAutoCommit = connection.getAutoCommit();
try {
Properties properties = new Properties();
properties.put("app.user_id", "0000");
connection.setAutoCommit(false);
connection.setClientInfo(properties);
String query = "insert into positions (name, description) values (?, ?)";
try(PreparedStatement statement = connection.prepareStatement(query)) {
statement.setString(1, position.getName());
statement.setString(2, position.getDescription());
statement.executeUpdate();
}
connection.commit();
}
catch (SQLException e) {
e.printStackTrace();
connection.rollback();
}
finally {
connection.setAutoCommit(isAutoCommit);
}
}
I get PSQLException (insert query is dependent on parameter and it does not pass)
org.postgresql.util.PSQLException: ERROR: unrecognized configuration parameter "app.user_id"
If I use PreparedStatement I get PSQLException with message ERROR: syntax error at or near "$1"
try(Connection connection = getDataSource().getConnection()) {
boolean isAutoCommit = connection.getAutoCommit();
try {
connection.setAutoCommit(false);
try(PreparedStatement statement = connection.prepareStatement("set local app.user_id to ?")) {
statement.setString(1, "0000");
statement.execute();
}
String query = "insert into positions (name, description) values (?, ?)";
try(PreparedStatement statement = connection.prepareStatement(query)) {
statement.setString(1, position.getName());
statement.setString(2, position.getDescription());
statement.executeUpdate();
}
connection.commit();
}
catch (SQLException e) {
e.printStackTrace();
connection.rollback();
}
finally {
connection.setAutoCommit(isAutoCommit);
}
}
The only way to go through is by directly executing the query with fixed values. But in doing so, I am forced to use a concatenation to build the query. And I do not want to do it.
try(Connection connection = getDataSource().getConnection()) {
boolean isAutoCommit = connection.getAutoCommit();
try {
connection.setAutoCommit(false);
try(Statement statement = connection.createStatement()) {
statement.execute("set local app.user_id to 0000");
}
String query = "insert into positions (name, description) values (?, ?)";
try(PreparedStatement statement = connection.prepareStatement(query)) {
statement.setString(1, position.getName());
statement.setString(2, position.getDescription());
statement.executeUpdate();
}
connection.commit();
}
catch (SQLException e) {
e.printStackTrace();
connection.rollback();
}
finally {
connection.setAutoCommit(isAutoCommit);
}
}
What is the right way to initialize such parameters?
I use PostgreSQL 11, JDBC 4.2 (with driver 42.2.5) and DBCP 2.5
Edit
I did it by calling set_config.
try(PreparedStatement statement = connection.prepareStatement("select set_config(?, ?, true)")) {
statement.setString(1, "app.user_id");
statement.setString(2, "0000");
statement.execute();
}
But the question remains. How to call SET in JDBC
I think you need to do this on the DataSource not the Connection.
In postgresql the only way I know of would be to downconvert. Something like:
DataSource myDS = getDataSource();
if (DataSource instanceof BaseDataSource.class) {
BaseDataSource pgDS = (BaseDataSource) myDS; // expose setProperty method
pgDS.setProperty("app.user_id", "0000");
}
where you place this in your application obviously depends upon many details not presented in your question.

Getting a true(1) or false(0) from specific sql statement

I need help with the code below and getting it to return a true or false value. Any and all help would be appreciated.
public synchronized static boolean checkCompanyName(String companyName,
Statement statement) {
try {
ResultSet res = statement
.executeQuery("SELECT `companyName` FROM `companys` WHERE companyName = '"
+ companyName + "';");
boolean containsCompany = res.next();
res.close();
return containsCompany;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
Try to make your query like this:
ResultSet res = statement.executeQuery("SELECT companyName FROM companys WHERE companyName = " + companyName);
Or you can either you PreparedStatement which is better then you did before
You should be using a PreparedStatement (for that end pass the Connection in to the method). Also, you should retrieve the value from the ResultSet and validate it matches your companyName. Something like
static final String query = "SELECT `companyName` FROM "
+ "`companys` WHERE companyName = ?";
public synchronized static boolean checkCompanyName(String companyName,
Connection conn) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(query);
ps.setString(1, companyName);
rs = ps.executeQuery();
if (rs.next()) {
String v = rs.getString(1);
return v.equals(companyName);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
}
}
}
return false;
}
Two comments:
You only need to check if there's at least one row matching your criteria, so you can use .first()
Your code is vulnerable to SQL Injection attacks. Please read this to learn more about it.
The easiest way to avoid SQL injection attacs is to use prepared statements. So let me strike two birds with a single stone and give you a solution using them:
/*
Check if the company exists.
Parameters:
conn - The connection to your database
company - The name of the company
Returns:
true if the company exists, false otherwise
*/
public static boolean checkCompanyName(Connection conn, String company) {
boolean ans = false;
try(PreparedStatement ps = conn.prepareStatement(
"select companyName from companies where companyName = ?"
) // The question mark is a place holder
) {
ps.setString(1, company); // You set the value for each place holder
// using setXXX() methods
try(ResultSet rs = ps.executeQuery()) {
ans = rs.first();
} catch(SQLException e) {
// Handle the exception here
}
} catch(SQLException e) {
// Handle the exception here
}
return ans;
}
Suggested reads:
Bobby Tables: A guide to preventing SQL injection
The Java Tutorials - JDBC: Using prepared statements

JDBC Choose Column

Here is my code:
import java.sql.*;
public class clazz{
public static void main(String[] args) {
try{
Connection con = DriverManager.getConnection("jdbc:mysql://localhost/database","root","password");
Statement stmt = (Statement) con.createStatement();
String insert = "INSERT INTO table VALUES ('value')";
stmt.executeUpdate(insert);
}catch(Exception e){
}
}
}
This works great and all, if there's only one column. How would I specify the column?
This works great and all, if there's only one column. How would I
specify the column?
Just specify the column name in the column list in your query.
String insert = "INSERT INTO table (colname1, colname2) VALUES ('value1','value2')";
Btw, I would recommend you to use PreparedStatement instead of Statement while executing SQL queries using JDBC in order to prevent SQL Injection.
Here is a demo.
create table t_customer (
id number(19, 0) not null,
first_name varchar2(50) not null,
last_name varchar2(50) not null,
last_login timestamp null,
comments clob null,
constraint pk_customer primary key(id)
)
public class InsertDemo {
private static final String CONNECTION_STRING =
"jdbc:oracle:thin:#oracle.devcake.co.uk:1521:INTL";
public static void main(String[] args) {
try {
Class.forName("oracle.jdbc.OracleDriver");
} catch (ClassNotFoundException e) {
return;
}
Connection connection;
try {
connection = DriverManager.getConnection(CONNECTION_STRING,
"PROSPRING", "x******6");
} catch (SQLException e) {
return;
}
PreparedStatement preparedStatement;
try {
preparedStatement = connection.prepareStatement(
"insert into t_customer (id, first_name, last_name, last_login, " +
"comments) values (?, ?, ?, ?, ?)");
} catch (SQLException e) {
return;
}
try {
preparedStatement.setLong(1, 1L);
preparedStatement.setString(2, "Jan");
preparedStatement.setString(3, "Machacek");
preparedStatement.setNull(4, Types.TIMESTAMP);
preparedStatement.setNull(5, Types.CLOB);
preparedStatement.executeUpdate();
} catch (SQLException e) {
return; // 1
}
try {
connection.commit();
} catch (SQLException e) {
return;
}
try {
connection.close();
} catch (SQLException e) {
// noop
}
}
}

Categories