Deferencing Null pointer - java

public class Model
{
public static Connection getConnection()
{
Connection conn = null;
try
{
Class.forName("oracle.jdbc.OracleDriver");
conn = DriverManager.getConnection("jdbc:oracle:thin:#localhost:1521:xe", "System", "system");
}
catch(ClassNotFoundException e)
{
e.printStackTrace();
}
catch(SQLException e)
{
e.printStackTrace();
}
return conn;
}
public static class Cart
{
public String itmName="";
public int howmany=0;
public static long itmQty=0, itmID=0;
public double itmPrice=0.0, itmCost=0.0, totalSum=0.0;
}
public static ArrayList<Cart> getCartDatabase(String user) throws Exception
{
Connection conn = getConnection();
String sql = "select * from userCarts where userID = '" + user + "'";
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rst = pstmt.executeQuery();
ArrayList<Cart> al = null;
Cart crt=null;
while(rst.next())
{
System.out.println("CPoint");
try
{
long p = rst.getLong("itemID");
crt.itmID = p; // This is the line thats creating the error
System.out.println(p + " is long! I guess...");
}
catch(NullPointerException e)
{
System.out.println("NPE Caught in Model");
}
System.out.println("CP 1 " + crt.itmID);
ArrayList<row> alr=null;
try
{
alr = Model.getStoreInventory();
}
catch(Exception e)
{
e.printStackTrace();
}
System.out.println("CP 2");
for(int i=0; i<alr.size(); i++)
{
crt.itmName = alr.get(i).itmName;
crt.itmPrice = alr.get(i).itmPrice;
crt.itmQty = alr.get(i).itmQty;
}
System.out.println("CP 3");
crt.howmany = rst.getInt("howmany");
crt.itmCost = crt.itmPrice*crt.howmany;
al.add(crt);
}
return al;
}
}
When I try to access this method of getCartFromDatabase, it gives a NullPointerException however I don't understand why it would do this. Moreover, I tried to make the class as a non static class too, but still it gave the same error:
"Possible deferencing Null Pointer"

Cart crt=null;
while(rst.next())
{
System.out.println("CPoint");
try
{
long p = rst.getLong("itemID");
crt.itmID = p; // This is the line thats creating the error
System.out.println(p + " is long! I guess...");
}
crt is null when you try to access crt.itemID. You have to assign it an instance first.
I think you may simply change the first line from the snippet to
Cart crt = new Cart();

Related

What is the right way to deal with the PreparedStatement in the Java program flow?

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.

Is the MySQL procedure in this Minecraft plugin correct?

To get an idea of what the basic structure looks like, I downloaded a money system including MySQL from Spigot and looked at the code.
public static boolean playerExists(String uuid) {
try {
ResultSet rs = Simplecoinsystem.mysql.query("SELECT * FROM CoinData WHERE UUID= '" + uuid + "'");
if (rs.next())
return (rs.getString("UUID") != null);
return false;
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
public static void createPlayer(String uuid) {
if (!playerExists(uuid))
Simplecoinsystem.mysql.update("INSERT INTO CoinData (UUID, COINS) VALUES ('" + uuid +
"', '" + Simplecoinsystem.getInstance().getConfig().getInt("startcoins") + "');");
}
public static Integer getCoins(String uuid) {
Integer i = Integer.valueOf(0);
if (playerExists(uuid)) {
try {
ResultSet rs = Simplecoinsystem.mysql.query("SELECT * FROM CoinData WHERE UUID= '" + uuid + "'");
if (rs.next())
Integer.valueOf(rs.getInt("COINS"));
i = Integer.valueOf(rs.getInt("COINS"));
} catch (SQLException e) {
e.printStackTrace();
}
} else {
createPlayer(uuid);
}
return i;
}
public static void setCoins(String uuid, Integer coins) {
if (playerExists(uuid)) {
Simplecoinsystem.mysql.update("UPDATE CoinData SET COINS= '" + coins + "' WHERE UUID= '" + uuid + "';");
} else {
createPlayer(uuid);
}
}
Am I correct that it is actually impractical to create a new entry with the uuid of the non-existent player after each query of the coins if the player does not exist?
Wouldn't this make it possible to flood the database with thousands of unnecessary entries by issuing, for example, a "/money (player)" command as an evil player/admin?
Couldn't I just ask when entering the server if the uuid is already stored and if not, just enter it? This way there would only be entries from players who have already been on the server before. Whether this needs great server performance, I'm not sure.
This is my first own MySQL class.
public class MySQL {
private String host, database, user, password;
private int port;
private Connection con;
public MySQL(String host, int port, String database, String user, String password) {
this.host = host;
this.port = port;
this.database = database;
this.user = user;
this.password = password;
connect();
}
public void connect() {
try {
con = DriverManager.getConnection("jdbc:mysql://" + host + ":" + port + "/" + database + "?autoReconnect=true", user, password);
System.out.println("&cDie MySQL Verbindung wurde erfolgreich aufgebaut!");
} catch (SQLException e) {
e.printStackTrace();
}
}
public void disconnect() {
try {
if(this.con != null) {
this.con.close();
System.out.println("§cDie MySQL Verbindung wurde erfolgreich beendet!");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void update(String query) {
try {
Statement st = con.createStatement();
st.executeUpdate(query);
st.close();
} catch (Exception e) {
e.printStackTrace();
connect();
}
}
public ResultSet qry(String query) {
ResultSet rs = null;
try {
Statement st = con.createStatement();
rs = st.executeQuery(query);
} catch (Exception e) {
e.printStackTrace();
connect();
}
return rs;
}
public Connection getConnection() {
return this.con;
}
}
Except for this part, both MySQL classes are built relatively the same.
This is the part that is in the MySQL class of the Spigot plugin.
Your code have multiple issues.
When the connection will be closed, next time you will have an error. In your Mysql class, I suggest you to do:
public Connection getConnection() {
if(con == null || con.isClosed())
connect();
return con;
}
Then, use it in all method like getConnection().prepareStatement().
You can be attacked with SQL Injection. To fix this, try to do something like:
PreparedStatement st = con.prepareStatement("SELECT * FROM CoinData WHERE UUID = ?");
st.setString(1, uuid.toString()); // Yes it start at 1 !!
st.executeUpdate();
With this, even with all values, you can't be attacked with injections.
You will have an error while getting coins:
if (rs.next()) // go to good line
Integer.valueOf(rs.getInt("COINS")); // useless convertion
i = Integer.valueOf(rs.getInt("COINS")); // error if no line.
You can just do:
if(rs.next())
i = rs.getInt("COINS");
If the column "UUID" is unique, you will not have duplicated lines.
Finally, about performance, it's better to do it one time: at login, instead of all time. You can also create an object stored in an hashmap to easier access to it, without using SQL, like that:
public static HashMap<UUID, Integer> coinsByPlayer = new HashMap<>();
OR:
public static HashMap<UUID, MyObject> coinsByPlayer = new HashMap<>();
public class MyObject {
private int coins = 0;
public MyObject(UUID uuid) {
// make SQL request to get data
}
public int getCoins() {
return coins;
}
public void setCoins(int next){
coins = next;
// here make "UPDATE" sql query
}
}
What do you say? Is it ok with the try/catch function? #Elikill58
public Connection getConnection() {
try {
if(con == null || con.isClosed()) {
connect();
}
} catch (SQLException e) {
e.printStackTrace();
}
return con;
}
edit:
public Connection getConnection_one() throws SQLException {
if(con == null || con.isClosed()) {
connect();
return con;
} else {
return con;
}
}

Data i've inserted into table does not replace old data, is there a way to replace data if it's already there?

Problem is the following: I am saving hashed password for a school project, however i am stuck on the syntax for the SQL statement to replace the data if it is already present. The table will only need to store a single username/password combination.
public class DatabaseManager {
String dbPath = "jdbc:sqlite:test.db";
public DatabaseManager () {
try {
Class.forName("org.sqlite.JDBC");
Connection conn = DriverManager.getConnection(dbPath);
if (conn != null) {
System.out.println("Connected to the database");
DatabaseMetaData dm = (DatabaseMetaData) conn.getMetaData();
// Setting up database
databaseSetup(conn);
boolean tempInsertion = databaseInsert("pancake", "house", conn);
// Inserting data
if (tempInsertion) {
System.out.println("Data insertion failed");
}
// Retrieving data
List<String> retrievedData = databaseSelect(conn);
if (retrievedData == null) {
System.out.println("Data extraction failed");
}
else {
System.out.println(retrievedData.size());
}
conn.close();
}
}
catch (ClassNotFoundException ex) {
ex.printStackTrace();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
private boolean databaseInsert(String username, String password, Connection conn) {
String sqlInsert = "INSERT OR REPLACE INTO login(username, password) VALUES(?,?)";
PreparedStatement prepStatement;
try {
prepStatement = conn.prepareStatement(sqlInsert);
prepStatement.setString(1, encrypt(username));
prepStatement.setString(2, encrypt(password));
prepStatement.executeUpdate();
} catch (SQLException e) {
return false;
}
return true;
}
private List<String> databaseSelect(Connection conn) {
List<String> tempList = new ArrayList<String>();
String sqlSelect = "SELECT * FROM login";
Statement stmt;
try {
stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sqlSelect);
tempList.add(rs.getString("username"));
tempList.add(rs.getString("password"));
int columnsNumber = rs.getMetaData().getColumnCount();
while (rs.next()) {
for (int i = 1; i <= columnsNumber; i++) {
if (i > 1) System.out.print(", ");
String columnValue = rs.getString(i);
System.out.print(columnValue + " " + rs.getMetaData().getColumnName(i));
}
System.out.println("");
}
} catch (SQLException e) {
return null;
}
return tempList;
}
private void databaseSetup( Connection conn) {
String sqlExpression = "CREATE TABLE login (username varchar(255), password varchar(255))";
try {
Statement statement = conn.createStatement();
statement.execute(sqlExpression);
} catch (SQLException e) {}
}
private String encrypt(String string) {
try {
MessageDigest exampleCrypt = MessageDigest.getInstance("SHA1");
exampleCrypt.reset();
exampleCrypt.update(string.getBytes("UTF-8"));
return convertByte(exampleCrypt.digest());
}
catch(NoSuchAlgorithmException e) {
System.out.println("Error, cannot encrypt string");
e.printStackTrace();
}
catch(UnsupportedEncodingException e) {
System.out.println("Error, cannot encrypt string");
e.printStackTrace();
}
return null;
}
private static String convertByte(final byte[] hash) {
Formatter formatter1 = new Formatter();
for (byte i : hash) {
formatter1.format("%02x", i);
}
String encryptedData = formatter1.toString();
formatter1.close();
return encryptedData;
}
}
The problem as stated, is that i'd like to only store a single password/username combination at a time, as a hash. However, when this happens it duplicates the hash combination, instead of replacing it.

Using HikariCP's connection pool the correct way

I been trying to develop a Minecraft server plugin where a player enters a command with some data, data is sent to database, or, a command that requests some data from database.
It's working, until a user starts using it more then a few times. I get a leakdetection error:
[HikariPool-2 housekeeper] WARN com.zaxxer.hikari.pool.ProxyLeakTask - Connection leak detection triggered for com.mysql.jdbc.JDBC4Connection#abc6eb, stack trace follows
[23:36:11 WARN]: java.lang.Exception: Apparent connection leak detected
Or I get an error that tells me that I have too many connections. (Sorry, I don't have that error at this moment)
This is the gist of my code. What am I doing improperly?
public class MochaModel {
private Latte instance = Latte.getInstance();
private Connection connection;
public MochaModel() {
}
public void createTable() {
BukkitRunnable r = new BukkitRunnable() {
#Override
public void run() {
try {
connection = Database.getConnection();
if (connection != null) {
String sql = "CREATE TABLE IF NOT EXISTS `mocha` ( " +
" `id` INT NOT NULL AUTO_INCREMENT ," +
"`uuid` VARCHAR(255) NOT NULL ," +
" `join_message` VARCHAR(255) NOT NULL ," +
" `quit_message` VARCHAR(255) NOT NULL ," +
" `change_points` INT NOT NULL," +
" `last_modified` TIMESTAMP NOT NULL," +
" PRIMARY KEY (`id`)" +
")";
PreparedStatement q = connection.prepareStatement(sql);
q.executeUpdate();
}
} catch(SQLException e) {
e.printStackTrace();
} finally {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
};
r.runTaskAsynchronously(instance);
}
public void setJoinMessage(String uuid, String message) {
ResultSet rs = getDataWithUUID(uuid);
String[] sqlValues = new String[2];
try {
if (!rs.isBeforeFirst()) {
String insertSql = "INSERT INTO `mocha` (`uuid`, `join_message`,`quit_message`, `change_points`, `last_modified`) VALUES (?, ?, '', 0, CURRENT_TIMESTAMP)";
sqlValues[0] = uuid;
sqlValues[1] = message;
insertData(insertSql, sqlValues);
} else {
while (rs.next()) {
String updateSql = "UPDATE `mocha` SET `join_message`=? WHERE `uuid`=?";
sqlValues[0] = message;
sqlValues[1] = uuid;
updateData(updateSql, sqlValues);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public void setQuitMessage(String uuid, String message) {
ResultSet rs = getDataWithUUID(uuid);
String[] sqlValues = new String[2];
try {
if (!rs.isBeforeFirst()) {
String insertSql = "INSERT INTO `mocha` (`uuid`, `join_message`,`quit_message`, `change_points`, `last_modified`) VALUES (?, '', ?, 0, CURRENT_TIMESTAMP)";
sqlValues[0] = uuid;
sqlValues[1] = message;
insertData(insertSql, sqlValues);
} else {
while (rs.next()) {
String updateSql = "UPDATE `mocha` SET `quit_message`=? WHERE `uuid`=?";
sqlValues[0] = message;
sqlValues[1] = uuid;
updateData(updateSql, sqlValues);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
private void updateData(String sql, String[] sqlValues) {
BukkitRunnable r = new BukkitRunnable() {
#Override
public void run() {
try {
connection = Database.getConnection();
if (connection != null) {
PreparedStatement q = connection.prepareStatement(sql);
q.setString(1, sqlValues[0]);
q.setString(2, sqlValues[1]);
System.out.println(q);
q.executeUpdate();
}
} catch(SQLException e) {
e.printStackTrace();
} finally {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
};
r.runTaskAsynchronously(instance);
}
private void updateChangePointsData(String sql, String[] sqlValues) {
BukkitRunnable r = new BukkitRunnable() {
#Override
public void run() {
try {
connection = Database.getConnection();
if (connection != null) {
PreparedStatement q = connection.prepareStatement(sql);
q.setInt(1, Integer.parseInt(sqlValues[0]));
q.setString(2, sqlValues[1]);
System.out.println(q);
q.executeUpdate();
}
} catch(SQLException e) {
e.printStackTrace();
} finally {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
};
r.runTaskAsynchronously(instance);
}
private void insertData(String sql, String[] sqlValues) {
BukkitRunnable r = new BukkitRunnable() {
#Override
public void run() {
try {
connection = Database.getConnection();
if (connection != null) {
PreparedStatement q = connection.prepareStatement(sql);
q.setString(1, sqlValues[0]);
q.setString(2, sqlValues[1]);
System.out.println(q);
q.executeUpdate();
}
} catch(SQLException e) {
e.printStackTrace();
} finally {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
};
r.runTaskAsynchronously(instance);
}
private ResultSet getDataWithUUID(String uuid) {
ResultSet result = null;
String sqlPlayer = "SELECT * FROM `mocha` WHERE `uuid` = ?";
try {
connection = Database.getConnection();
if (connection != null) {
PreparedStatement q = connection.prepareStatement(sqlPlayer);
q.setString(1, uuid);
result = q.executeQuery();
}
} catch(SQLException e) {
e.printStackTrace();
}
return result;
}
public String getMessage(String uuid, String messageType) {
ResultSet rs = getDataWithUUID(uuid);
String message = null;
try {
if (!rs.isBeforeFirst()) {
message = null;
} else {
while (rs.next()) {
if (messageType.equalsIgnoreCase("getjoin")) {
message = rs.getString("join_message");
} else if (messageType.equalsIgnoreCase("getquit")) {
message = rs.getString("quit_message");
}
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return message;
}
public int getChangePoints(String uuid) {
ResultSet rs = getDataWithUUID(uuid);
int changePoints = 0;
try {
if (!rs.isBeforeFirst()) {
changePoints = 0;
} else {
while (rs.next()) {
changePoints = rs.getInt("change_points");
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return changePoints;
}
public void removeChangePoints(String uuid, int amount) {
int changePoints = getChangePoints(uuid);
String[] sqlValues = new String[2];
if (changePoints >= amount) {
String updateSql = "UPDATE `mocha` SET `change_points`=? WHERE `uuid`=?";
sqlValues[0] = String.valueOf((changePoints-amount));
sqlValues[1] = uuid;
updateData(updateSql, sqlValues);
}
}
public void addChangePoints(String uuid, int amount) {
int changePoints = getChangePoints(uuid);
String[] sqlValues = new String[2];
String updateSql = "UPDATE `mocha` SET `change_points`=? WHERE `uuid`=?";
sqlValues[0] = String.valueOf((changePoints+amount));
sqlValues[1] = uuid;
updateChangePointsData(updateSql, sqlValues);
}
}
My DB Class:
public class Database {
private static Latte instance = Latte.getInstance();
private static Config config = new Config();
private static HikariConfig dbConfig;
static {
dbConfig = new HikariConfig();
dbConfig.setJdbcUrl("jdbc:mysql://localhost:3306/" + config.get("database.database"));
dbConfig.setUsername(config.get("database.username"));
dbConfig.setPassword(config.get("database.password"));
dbConfig.setDriverClassName("com.mysql.jdbc.Driver");
dbConfig.addDataSourceProperty("cachePrepStmts", "true");
dbConfig.addDataSourceProperty("prepStmtCacheSize", "250");
dbConfig.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
}
private static HikariDataSource ds = new HikariDataSource(dbConfig);
public static Connection getConnection() {
try {
ds.setIdleTimeout(60000);
ds.setConnectionTimeout(60000);
ds.setValidationTimeout(3000);
ds.setLoginTimeout(5);
ds.setMaxLifetime(60000);
ds.setMaximumPoolSize(20);
ds.setLeakDetectionThreshold(5000);
return ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
When opening a Connection you also need to close it. However you are storing the Connection in a instance variable. Which, for certain paths in your code, might result in multiple Connection instances being used. Due the the storage in the instance variable only the last one used will get closed, all the others are leaked.
Instead you want to make it local or hide parts of the complexity. You could rewrite your Database class to something like this.
Note: Assuming Java 8 here!
public class Database {
private static Latte instance = Latte.getInstance();
private static Config config = new Config();
private static HikariConfig dbConfig;
static {
dbConfig = new HikariConfig();
dbConfig.setJdbcUrl("jdbc:mysql://localhost:3306/" + config.get("database.database"));
dbConfig.setUsername(config.get("database.username"));
dbConfig.setPassword(config.get("database.password"));
dbConfig.setDriverClassName("com.mysql.jdbc.Driver");
dbConfig.addDataSourceProperty("cachePrepStmts", "true");
dbConfig.addDataSourceProperty("prepStmtCacheSize", "250");
dbConfig.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
}
private static HikariDataSource ds = new HikariDataSource(dbConfig);
public static <T> T execute(ConnectionCallback<T> callback) {
try (Connection conn = ds.getConnection()) {
return callback.doInConnection(conn);
} catch (SQLException e) {
throw new IllegalStateException("Error during execution.", e);
}
}
public static interface ConnectionCallback<T> {
public T doInConnection(Connection conn) throws SQLException;
}
}
Notice no more getConnection and due to the try-with-resources the connection will get closed automatically.
You can now call this method with instances of ConnectionCallback instead of getting the Connection and manage it yourself.
Now the code that uses the Connection can be refactored, to something like this. (Notice no more catches, closes etc. all that is handled in the Database.execute method.
private void updateData(String sql, String[] sqlValues) {
BukkitRunnable r = new BukkitRunnable() {
#Override
public void run() {
Database.execute( (conn) -> {
PreparedStatement q = conn.prepareStatement(sql);
q.setString(1, sqlValues[0]);
q.setString(2, sqlValues[1]);
System.out.println(q);
q.executeUpdate();
return null;
}} );
};
r.runTaskAsynchronously(instance);
}
This code will close the Connection after each use (and you cannot forget to close it).

Using profiling tool to find the exact reason for outOfMemoryError in the java code

Hi i have a java program for accessing remote as well as local databases at the same time using JDBC.
But i am getting this exception:
->Exception in thread "Thread-1964" java.lang.OutOfMemoryError: Java heap space
Now i wanted to use any profiling tool through whic i can get exact reason for this exception in my code.
This is my java program
public class DBTestCases{
Connection localConnection;
Connection remoteConnection;
Connection localCon;
Connection remoteCon;
List<Connection> connectionsList;
String driver = "com.mysql.jdbc.Driver";
String user = "root";
String password = "root";
String dbName = "myDB";
String connectionUrl1= "jdbc:mysql://11.232.33:3306/"+dbName+"?user="+user+"&password="+password+"&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&maxReconnects=10";
String connectionUrl2= "jdbc:mysql://localhost:3306/"+dbName+"?user="+user+"&password="+password+"&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&maxReconnects=10";
public List<Connection> createConnection() {
try {
Class.forName(driver);
localCon = DriverManager.getConnection(connectionUrl2);
if(localCon != null)
System.out.println("connected to remote database at : "+new Date());
remoteCon = DriverManager.getConnection(connectionUrl1);
if(remoteCon != null)
System.out.println("connected to local database at : "+new Date());
connectionsList = new ArrayList<Connection>( 2 );
connectionsList.add( 0 , localCon );
connectionsList.add( 1 , remoteCon );
} catch(ClassNotFoundException cnfe) {
cnfe.printStackTrace();
} catch(SQLException sqle) {
sqle.printStackTrace();
}
return connectionsList;
}
public void insert(){
Runnable runnable = new Runnable(){
public void run() {
PreparedStatement ps1 = null;
PreparedStatement ps2 = null;
String sql = "insert into user1(name, address, created_date)" +
" values('johnsan', 'usa', '2013-08-04')";
if(remoteConnection != null&&localConnection != null) {
System.out.println("Database Connection Is Established");
try {
ps1 = remoteConnection.prepareStatement(sql);
ps2 = localConnection.prepareStatement(sql);
int i = ps1.executeUpdate();
int k = ps2.executeUpdate();
if(i > 0) {
System.out.println("Data Inserted into remote database table Successfully");
}
if(k > 0) {
System.out.println("Data Inserted into local database table Successfully");
}
} catch (SQLException s) {
System.out.println("SQL code does not execute.");
s.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("Inserting values in db");
}
};
Thread thread = new Thread(runnable);
thread.start();
}
public void retrieve(){
Runnable runnable = new Runnable(){
public void run() {
try {
Statement st1 = localConnection.createStatement();
Statement st2 = remoteConnection.createStatement();
ResultSet res1 = st1.executeQuery("SELECT * FROM user1");
ResultSet res2 = st2.executeQuery("SELECT * FROM user1");
System.out.println("---------------------------Local Database------------------------");
while (res1.next()) {
Long i = res1.getLong("userId");
String s1 = res1.getString("name");
String s2 = res1.getString("address");
java.sql.Date d = res1.getDate("created_date");
System.out.println(i + "\t\t" + s1 + "\t\t" + s2 + "\t\t"+ d);
}
System.out.println("------------------------Remote Database---------------------");
while (res2.next()) {
Long i = res2.getLong("userId");
String s1 = res2.getString("name");
String s2 = res2.getString("address");
java.sql.Date d = res2.getDate("created_date");
System.out.println(i + "\t\t" + s1 + "\t\t" + s2 + "\t\t"+ d);
}
} catch (SQLException s) {
System.out.println("SQL code does not execute.");
s.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
}
public static void main(String[] args) {
DBTestCases dbTestCases = new DBTestCases();
List l = dbTestCases.createConnection();
dbTestCases.localConnection = (Connection)l.get(0);
dbTestCases.remoteConnection = (Connection)l.get(1);
for(;;) {
dbTestCases.insert();
dbTestCases.countRows();
dbTestCases.retrieve();
}
}
}
PLease can anybody help me which is the best tool to use and how i have to use it,and any links for this.i am using linux operating system.
I think i am starting a new thread for each call of method can anybody suggest how to close thread before starting it again or use thred pool..
Thankyou in advance.
The same problem has happened to me once. The problem is your java reserved heap memory. This heap memory is configurable through one of the java config files; both it's minimum and maximum amount.
There are a lots of profilers.
I have been using Visual VM for a while and that's useful for me, at least. It's easy to use and pretty powerful tool as well.
Here's the link:
http://visualvm.java.net
Not sure which IDE you are using. But there's nothing do do with OutOfMemoryError which mean that it's a RuntimeException.
A little bit of offtopic (because I'm not going to suggest you a profiler), but the problem, I think, comes from these lines
ps1 = remoteConnection.prepareStatement(sql);
ps2 = localConnection.prepareStatement(sql);
int i = ps1.executeUpdate();
int k = ps2.executeUpdate();
and
Statement st1 = localConnection.createStatement();
Statement st2 = remoteConnection.createStatement();
ResultSet res1 = st1.executeQuery("SELECT * FROM user1");
ResultSet res2 = st2.executeQuery("SELECT * FROM user1");
After you create PreparedStatement and use it, you should close it. Either you should use close() method when you don't need it anymore or use try-with-resources (it use close() automatically). Java Tutorial on statements and on try-with-resources. Also consider reading this and this topics.
A would rewrite your code something like this (didn't actually tried it, but it compiles and it supposed to eliminate problems with memory leaks):
public class DBTestCases {
Connection localConnection;
Connection remoteConnection;
Connection localCon;
Connection remoteCon;
List<Connection> connectionsList;
String driver = "com.mysql.jdbc.Driver";
String user = "root";
String password = "root";
String dbName = "myDB";
String connectionUrl1= "jdbc:mysql://11.232.33:3306/"+dbName+"?user="+user+"&password="+password+"&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&maxReconnects=10";
String connectionUrl2= "jdbc:mysql://localhost:3306/"+dbName+"?user="+user+"&password="+password+"&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&maxReconnects=10";
public List<Connection> createConnection() {
try {
Class.forName(driver);
localCon = DriverManager.getConnection(connectionUrl2);
if(localCon != null)
System.out.println("connected to remote database at : "+new Date());
remoteCon = DriverManager.getConnection(connectionUrl1);
if(remoteCon != null)
System.out.println("connected to local database at : "+new Date());
connectionsList = new ArrayList<Connection>( 2 );
connectionsList.add( 0 , localCon );
connectionsList.add( 1 , remoteCon );
} catch(ClassNotFoundException cnfe) {
cnfe.printStackTrace();
} catch(SQLException sqle) {
sqle.printStackTrace();
}
return connectionsList;
}
public void insert(){
Runnable runnable = new Runnable(){
public void run() {
PreparedStatement ps1 = null;
PreparedStatement ps2 = null;
String sql = "insert into user1(name, address, created_date)" +
" values('johnsan', 'usa', '2013-08-04')";
if(remoteConnection != null&&localConnection != null) {
System.out.println("Database Connection Is Established");
try {
ps1 = remoteConnection.prepareStatement(sql);
ps2 = localConnection.prepareStatement(sql);
int i = ps1.executeUpdate();
int k = ps2.executeUpdate();
if(i > 0) {
System.out.println("Data Inserted into remote database table Successfully");
}
if(k > 0) {
System.out.println("Data Inserted into local database table Successfully");
}
} catch (SQLException s) {
System.out.println("SQL code does not execute.");
s.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
} finally {
if (ps1 != null) {
try {
ps1.close();
} catch (SQLException ex) {
System.out.println("Cannot close ps1 statement.");
}
}
if (ps2 != null) {
try {
ps2.close();
} catch (SQLException ex) {
System.out.println("Cannot close ps2 statement.");
}
}
}
}
System.out.println("Inserting values in db");
}
};
Thread thread = new Thread(runnable);
thread.start();
}
public void retrieve(){
Runnable runnable = new Runnable(){
public void run() {
Statement st1 = null;
Statement st2 = null;
ResultSet res1 = null;
ResultSet res2 = null;
try {
st1 = localConnection.createStatement();
st2 = remoteConnection.createStatement();
res1 = st1.executeQuery("SELECT * FROM user1");
res2 = st2.executeQuery("SELECT * FROM user1");
System.out.println("---------------------------Local Database------------------------");
while (res1.next()) {
Long i = res1.getLong("userId");
String s1 = res1.getString("name");
String s2 = res1.getString("address");
java.sql.Date d = res1.getDate("created_date");
System.out.println(i + "\t\t" + s1 + "\t\t" + s2 + "\t\t"+ d);
}
System.out.println("------------------------Remote Database---------------------");
while (res2.next()) {
Long i = res2.getLong("userId");
String s1 = res2.getString("name");
String s2 = res2.getString("address");
java.sql.Date d = res2.getDate("created_date");
System.out.println(i + "\t\t" + s1 + "\t\t" + s2 + "\t\t"+ d);
}
} catch (SQLException s) {
System.out.println("SQL code does not execute.");
s.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
} finally {
if (res1 != null) {
try {
res1.close();
} catch (SQLException ex) {
System.out.println("Cannot close res1 result set.");
}
}
if (st1 != null) {
try {
st1.close();
} catch (SQLException ex) {
System.out.println("Cannot close st1 statement.");
ex.printStackTrace();
}
}
if (res2 != null) {
try {
res2.close();
} catch (SQLException ex) {
System.out.println("Cannot close res2 result set.");
}
}
if (st2 != null) {
try {
st2.close();
} catch (SQLException ex) {
System.out.println("Cannot close st2 statement.");
ex.printStackTrace();
}
}
}
}
};
Thread thread = new Thread(runnable);
thread.start();
}
public static void main(String[] args) {
DBTestCases dbTestCases = new DBTestCases();
List l = dbTestCases.createConnection();
dbTestCases.localConnection = (Connection)l.get(0);
dbTestCases.remoteConnection = (Connection)l.get(1);
for(;;) {
dbTestCases.insert();
dbTestCases.countRows();
dbTestCases.retrieve();
}
}
}
There is also another problem with these statements:
ResultSet res1 = st1.executeQuery("SELECT * FROM user1");
ResultSet res2 = st2.executeQuery("SELECT * FROM user1");
I should never (with very rare exceptions) read entire table without using conditions in WHERE. Also if you need only one column created_date, then you must rewrite it like this:
ResultSet res1 = st1.executeQuery("SELECT created_date FROM user1");
ResultSet res2 = st2.executeQuery("SELECT created_date FROM user1");
But I didn't actually replaced it in a full listing, because it can be just 'quick and dirty' code. :)

Categories