I have DAO class with methods getting and sending data.
I'm catching Exceptions inside SQL requests, so I need to declare connection variables outside of try parenthesis.
every method looks lookes like this:
public Role getRole(int roleId) {
Connection connection = null;
ResultSet rs = null;
PreparedStatement statement = null;
Role role = null;
try {
connection = dataSource.getConnection();
statement = connection.prepareStatement("select ROLE_ID, ROLE_TEXT from ROLES WHERE ROLE_ID = :1");
statement.setInt(1, roleId);
rs = statement.executeQuery();
rs.next();
role = roleMapper.mapRow(rs, 1);
} catch (SQLException e) {
} finally {
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(statement);
JdbcUtils.closeConnection(connection);
return role;
}
}
But there's problem. Finbugs giving me an error, saying:
Load of known null value in DAO.getRole
and
may fail to clean up java.sql.Statement
So what should I do to avoid that?
The getRole can return null.
Furthermore:
if (rs.next()) {
role = roleMapper.mapRow(rs, 1);
}
I prefer another notation. And the error solution unfortunately consists of either letting getRole throw an exception (best) or letting return an Optional<Role>
//public Role getRole(int roleId) throws SQLException {
public Optional<Role> getRole(int roleId) {
try (Connection connection = dataSource.getConnection();
PreparedStatement statement =
connection.prepareStatement(
"select ROLE_ID, ROLE_TEXT from ROLES WHERE ROLE_ID = :1")) {
statement.setInt(1, roleId);
try (ResultSet rs = statement.executeQuery()) {
if (rs.next()) {
return roleMapper.mapRow(rs, 1);
}
}
} catch (SQLException e) { //
Logger.getLogger(getClass().getName()).log(Level.SEVERE, "ID: " + roleId, e); //
}
return Optional.empty(); //
}
Related
There are two methods in which the PreparedStatement is used.
The first method is called in the second method.
First method:
protected List<String> findResultsByMandantId(Long mandantId) {
List<String> resultIds = new ArrayList<>();
ResultSet rs;
String sql = "SELECT result_id FROM results WHERE mandant_id = ?";
PreparedStatement statement = getPreparedStatement(sql, false);
try {
statement.setLong(1, mandantId);
statement.execute();
rs = statement.getResultSet();
while (rs.next()) {
resultIds.add(rs.getString(1));
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
return resultIds;
}
Second method:
protected void findResultLineEntityToDelete(Long mandantId, String title, String context) {
List<String> resultIds = findResultsByMandantId(mandantId);
String [] resultIdsArr = resultIds.toArray(String[]::new);
ResultSet rs;
//String sql = "SELECT * FROM resultline WHERE result_id in (SELECT result_id FROM results WHERE mandant_id =" + mandantId + ")";
String sql = "SELECT * FROM resultline WHERE result_id in (" + String.join(", ", resultIdsArr)+ ")";
PreparedStatement statement = getPreparedStatement(sql, false);
try {
statement.execute();
rs = statement.getResultSet();
while (rs.next()) {
if (rs.getString(3).equals(title) && rs.getString(4).equals(context)) {
System.out.println("Titel: " + rs.getString(3) + " " + "Context: " + rs.getString(4));
}
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
The class in which both methods are located extends the JDBCBaseManager.
JDBCBaseManager:
private final String url = "jdbc:mysql://localhost:3306/database";
private final String userName = "root";
private final String password = "";
private Connection connection = null;
private PreparedStatement preparedStatement = null;
private int batchSize = 0;
public JDBCBaseManager() {
// Dotenv env = Dotenv.configure().directory("./serverless").load();
// url = env.get("DB_PROD_URL");
// userName = env.get("DB_USER");
// password = env.get("DB_PW");
}
public void getConnection() {
try {
if (connection == null) {
connection = DriverManager.getConnection(url, userName, password);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public PreparedStatement getPreparedStatement(String sql, boolean returnGeneratedKeys) {
try {
if (connection == null) {
getConnection();
}
if (preparedStatement == null) {
if (!returnGeneratedKeys) {
preparedStatement = connection.prepareStatement(sql);
} else {
preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
connection.setAutoCommit(false);
}
}
return preparedStatement;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void closeConnection() {
try {
if (connection != null && !connection.isClosed()) {
System.out.println("Closing Database Connection");
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void startBatch(int batchSize) throws SQLException {
connection.setAutoCommit(false);
setBatchSize(batchSize);
}
public void commit() {
try {
if (connection != null && !connection.isClosed()) {
connection.commit();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public int getBatchSize() {
return batchSize;
}
public void setBatchSize(int batchSize) {
this.batchSize = batchSize;
}
The ResultSet in the second method still contains the results from the first method.
I already tried to close the connection and open it again before the second method is executed, but then I get the errors:
java.sql.SQLException: No operations allowed after statement closed.
java.sql.SQLNonTransientConnectionException: No operations allowed
after connection closed.
Can you tell me how to deal with the statement correctly in this case? Is my BaseManager incorrectly structured?
Here lies the error
public JDBCBaseManager() {
private PreparedStatement preparedStatement = null;
public PreparedStatement getPreparedStatement(String sql, boolean returnGeneratedKeys) {
try {
......
if (preparedStatement == null) {
if (!returnGeneratedKeys) {
preparedStatement = connection.prepareStatement(sql);
} else {
preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
connection.setAutoCommit(false);
}
}
return preparedStatement;
You build the prepare statement only the first time the method getPreparedStatement is called because only the first time the field preparedStatement is null. Every next time you call the method getPreparedStatement you receive the previous preparedStatement from the previous SQL and not the new one.
Remove the check for if (preparedStatement == null) {
You need to build a new preparedStatement every time you want to execute a new SQL.
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.
I am trying to launch a class only if a select query returned at least a row.
My call to thequery function noted below:
results=thequery("SELECT `username`,`numberpoints` FROM `highscores` WHERE `username` = '"+name+"'");//send query that checks if username exist
if(!results.next()) {
BallTrial trial = new BallTrial();
}
thequery function:
public ResultSet thequery(String query){
PreparedStatement statement = null;
ResultSet rs = null;
Connection con=null;
Statement st=null;
try {
con=DriverManager.getConnection("jdbc:mysql://localhost/scores","root","");
statement = con.prepareStatement(query);
rs= statement.executeQuery();
while(rs.next()){
System.out.println(rs.getString(1)+" "+rs.getString(2));
return rs;
}}
catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("you have not accessed the data base");
}
return null;
}
I am getting a null pointer exception at if(!results.next()) {, can anyone clarify what is my mistake here?
You should add the null check condition before calling next, the condition needs to be tweaked something around the lines: results != null && !results.next()
You need to make a couple of changes:
From thequery method, return ResultSet (i.e. rs) instead of returning null. Returning null will cause NPE. Also, remove while(...) as it needs to be done by the caller, e.g.
public ResultSet thequery(String query){
PreparedStatement statement = null;
ResultSet rs = null;
Connection con=null;
Statement st=null;
try {
con=DriverManager.getConnection("jdbc:mysql://localhost/scores","root","");
statement = con.prepareStatement(query);
rs= statement.executeQuery();
return rs;
catch (SQLException e) {
e.printStackTrace();
System.out.println("you have not accessed the data base");
throw e;
}
}
In the caller method, check whether ResultSet contains any records by calling rs.next(), e.g.:
results = thequery(..)
if(!results.next()){
BallTrial trial = new BallTrial();
}
while(true) {
if (!results.next()){
System.out.prinln("result has no data");
}else{
System.out.prinln("result has some data");
}
}
I am trying to mock a object in the junit test file. I am getting value in test file
public void TestMethod() throws SQLException{
String projectID = "Test";
Connection connectionMock = Mockito.mock(Connection.class);
PreparedStatement preparedStatementMock = Mockito.mock(PreparedStatement.class);
ResultSet resultSetMock = Mockito.mock(ResultSet.class);
mockStatic(Manager.class);
Worker workerMock = Mockito.mock(Worker.class);
mockingDetails(workerMock);
mock(PreparedStatement.class);
mock(ResultSet.class);
when(workerMock.getProjectId())
.thenReturn(projectID);
when(Manager.getDatabaseConnection())
.thenReturn(connectionMock);
when(connectionMock.prepareStatement(""))
.thenReturn(preparedStatementMock);
when(preparedStatementMock.executeQuery())
.thenReturn(resultSetMock);
//mockStatic(Manager.class);
System.out.println("workerMock--------->"+workerMock);
System.out.println("prepared stmt--------->"+preparedStatementMock);
assertNotNull(service.getProtocol());
}
If I print here I am getting value worker and prepared statement like this value
"Mock for Worker, hashCode: 853323835"
If I print same in my real method, I am getting null for both worker and prepared statement. I have used annotations
#RunWith(PowerMockRunner.class)
#PrepareForTest(Manager.class)
in the test class
public String Method() throws SQLException
{
Connection conn = Manager.getDatabaseConnection();
String protocol = null;
StringBuilder qryBuilder = new StringBuilder("MyQuery");
qryBuilder.append("condition = ?");
System.out.println("worker.getProjectId()------------->"+worker);
System.out.println("connection------------->"+conn);
PreparedStatement pst = conn.prepareStatement(qryBuilder.toString());
System.out.println("pst------------->"+pst);
pst.setString(1, worker.getProjectId());
try {
ResultSet rset = pst.executeQuery();
try {
while (rset.next()) {
protocol = rset.getString(1);
}
rset.close();
} finally {
pst.close();
}
} catch (SQLException e) {
throw e;
}
return protocol;
}
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