JDBC with MySQL really slow, why - java

I have a problem with a really slow connection between my Java code and a MySQL remote Database when i use multiple query.
This is my code
ArrayList<Server_Log> ar =Server_Log_Utilities.getBy2Dates(cmb_date.getSelectedItem() + "", cmb_date2.getSelectedItem() + "");
for (int c = 0; c < ar.size(); c++) {
Server_Log sl = ar.get(c);
String username = User_Utilities.getUserName(sl.getUser() + "");
String row[] = {sl.getDate(), sl.getTime(), username, sl.getReff(), sl.getDescription()};
}
but I user this code data will load fast
ArrayList<Server_Log> ar =Server_Log_Utilities.getBy2Dates(cmb_date.getSelectedItem() + "", cmb_date2.getSelectedItem() + "");
for (int c = 0; c < ar.size(); c++) {
Server_Log sl = ar.get(c);
String row[] = {sl.getDate(), sl.getTime(), sl.getReff(), sl.getDescription()};
}
this is User_Utilities.getUserName(sl.getUser() + ""); Method
public static String getUserName(String id) {
String UserName="";
try {
Connection con = new DBCon().getConnection();
ResultSet rst = DBHandle.getData(con, "SELECT username FROM user WHERE id='" + id + "'");
while (rst.next()) {
UserName =rst.getString(1);
}
con.close();
} catch (Exception ex) {
Logger.getLogger(User_Utilities.class.getName()).log(Level.SEVERE, null, ex);
}
return UserName;
}
Server_Log_Utilities.getBy2Dates(cmb_date.getSelectedItem() + "",
cmb_date2.getSelectedItem() + ""); Method
public static ArrayList getBy2Dates(String date1, String date2) {
try {
ar = new ArrayList<>();
Connection con = new DBCon().getConnection();
ResultSet rst = DBHandle.getData(con, "SELECT * FROM server_log WHERE date BETWEEN '" + date1 + "' AND '" + date2 + "' ORDER BY `date`");
while (rst.next()) {
Server_Log ci = new Server_Log();
ci.setId(rst.getInt(1));
ci.setDate(rst.getString(2));
ci.setTime(rst.getString(3));
ci.setReff(rst.getString(4));
ci.setDescription(rst.getString(5));
ci.setUser(rst.getInt(6));
ar.add(ci);
}
con.close();
} catch (Exception ex) {
Logger.getLogger(Student_Utilities.class.getName()).log(Level.SEVERE, null, ex);
}
return ar;
}

When accessing a remote database, especially over a slow link, the number of SQL statements executed is very important.
This is why the JDBC API support concepts like statement batching.
In your case, you're calling getUserName for every record in ar. Consider ways to reduce the number of calls.
Example 1: If user is usually the same, or only a few users are generating log entries, caching the user names would eliminate redundant lookup.
Example 2: Rather than looking up the user in the client, modify the Server_Log_Utilities.getBy2Dates to add a JOIN to the User table. This way, no extra turn-arounds to database will be needed.
Example 3: Instead of calling getUserName individually in a loop, collect the user ids, and lookup the names in a batch. Use either a JDBC batch of multiple SELECT statements, or use a single statement with UserId IN (?,?,?,?,...).

Related

JSP won't display string return value

I have a class called "Database" that is working perfectly well. It queries a database and returns the results as a string. When I call the class from my "Test" class it returns the results string and I can print it with System.out.println().
I'm trying to use this class on a JSP page using the same two lines of code to instantiate the class and get the string output. When I try to output on the JSP page I get nothing. What am I doing wrong? I'm completely stumped.
Class Code:
public class Database {
static String[][] reservations = new String[7][20];
public Database (String theDate) {
String url = "jdbc:mysql://myurl.com:3306/";
String driver = "com.mysql.cj.jdbc.Driver";
String user = "myusername";
String pass = "mypassword";
String db = "class";
String options = "?useSSL=false";
try (Connection conn = DriverManager.getConnection(url + db + options, user, pass);
Statement statement = conn.createStatement()) {
String query = "select reservation.first, reservation.last, startday, numberofdays, guides.first as guidefirst, guides.last as guidelast, locations.location from reservation left join guides on reservation.guide = idguides left join locations on reservation.location = idlocations where StartDay >= " + theDate;
ResultSet rs = statement.executeQuery(query);
int row = 0;
while (rs.next()) {
reservations[0][row] = rs.getString("first");
reservations[1][row] = rs.getString("last");
row++;
}
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public String getStringRes () {
String returnString = "";
for(int i=0; i<20; i++) {
if (reservations[0][i] != null) {
returnString += i + " " + reservations[0][i];
returnString += " " + reservations[1][i] + "\n";
}
}
return returnString;
} }
JSP Code:
<%# page import="mypackage.Database" %>
<%
Database db = new Database("2015-07-01");
String str = db.getStringRes();
%>
<%= str %>
Your code contains 3 mistakes.
The most problematic
} catch (SQLException e) {
e.printStackTrace();
}
Don't ever do that. Go into your IDE settings and eliminate this template. It should be throw new RuntimeException("Unhandled", e); instead.
Here's what happened: Your SQL statement is erroneous (in two ways, even). This causes an exception. Your code handles this by ignoring it (it's printed, but, your code finishes normally). Hence, the string result remains blank (because it starts out that way and the code that is supposed to give it its real value never ran, due to the exception that you ignored).
Your SQL is broken.
The actual explanation is that date literals need to be in quotes, in SQL. Yours is not.
But that's not how you pass parameters into SQL.
The bigger issue is that passing any value like this into an SQL means your machine will be hacked in a matter of days. That's called 'SQL injection'. You don't want it. The solution is preparedstatements, where you let the JDBC driver and/or the database take care of escaping or otherwise passing string data without the SQL db engine trying to interpret it as SQL (Because, obviously, letting arbitrary users type stuff in that your DB engine then treats as SQL means you're just waiting for someone to construct some SQL such that your db engine ends up executing DROP TABLE reservation CASCADE; EXECUTE 'FORMAT C: /Y'; --.
Minor style nit
Doing business logic in constructors is a bad idea. The query should be done from getStringRes, most likely. That's also a bit of a crazy method name. Not very informative.
Which gets us to...
try (Connection conn = DriverManager.getConnection(url + db + options, user, pass);
PreparedStatement statement = conn.prepareStatement("SELECT reservation.first, .... FROM .... WHERE StartDay >= ?")) {
statement.setString(1, theDate);
try (ResultSet rs = statement.executeQuery()) {
...
}
} catch (SQLException e) {
throw new RuntimeException("unhandled", e);
}

SQL parameter issue Jdbc

I am taking input from user and storing in two different variables. I am binding the parameters with my sql statement. When i run the code its giving issue on concatenating part of query.
String CityA= null;
String CityB= null;
try {
CityA = readEntry(in, "Enter Origin City : ");
CityB = readEntry(in, "Enter Destination City : ");
// We treat this drop table specially to allow it to fail
// as it will the very first time we run this program
try {
String q = "SELECT f.FLNO,f.DISTANCE,TIMEDIFF(f.arrives,f.departs)
as Duration FROM FLIGHTS F"
+ " WHERE F.ORIGIN = "+CityA;
+ "AND f.DESTINATION = "+CityB;
System.out.println(q);
rset = stmt.executeQuery(q);
while (rset.next()) {
System.out.println(rset.getInt("FLNO") + ","
+ rset.getInt("Distance") + ","
+ rset.getTime("Duration"));
}
System.out.println("Done");
}
catch (SQLException e) {
// assume not there yet, so OK to continue
}
finally {
stmt.close();
}
Please find the code for query:-
Basically you missed the space between the CityA and AND
String q = "SELECT f.FLNO,f.DISTANCE,TIMEDIFF(f.arrives,f.departs) as Duration FROM FLIGHTS F"
+ " WHERE F.ORIGIN = '"+CityA+"' ";
+ "AND f.DESTINATION = '"+CityB+"'";
There is a typo in your query string - you missed the space between 'Los-Angeles' and AND.

How to add more `?` automatically in java in accordance with whatever amount of columns will be placed in the interface

I'm trying to create an api so I can use it to whatever project I need to create. I'm still new to java so I'm sorry in advance for the wrong codes. Anyways, I've got here this code that has the CRUD statements (except for Read/Retrieve).
import java.sql.*;
public class KitApiNew {
public static void main(String[] args) throws SQLException {
Connection dbConnection = null;
PreparedStatement PSInsert = null;
PreparedStatement PSUpdate = null;
PreparedStatement PSDelete = null;
PreparedStatement PSStatement = null;
String DBTable = "fruits";
String DBColumnSet = "fruit";
String DBID = "23";
String DBColumnSingle = "fruit";
String DBChoice ="insert";
String DBCS = "";
String insertTable = "INSERT INTO " + DBTable + "(" +DBColumnSet+ ")" + "VALUES" + "(?)";
String updateTable = "UPDATE " + DBTable + " SET " + DBColumnSingle + " = ?" + " WHERE id = ?";
String deleteTable = "DELETE FROM " + DBTable + " WHERE id = ?";
String statementTable = "INSERT INTO fruits(fruit) VALUES('grapes')";
try{
dbConnection = getDBConnection();
dbConnection.setAutoCommit(false);
if(DBChoice.equals("insert")){
//for insert
PSInsert = dbConnection.prepareStatement(insertTable);
PSInsert.setString(1, "Orange");
PSInsert.executeUpdate();
dbConnection.commit();
}
if(DBChoice.equals("update")){
//for update
PSUpdate = dbConnection.prepareStatement(updateTable);
PSUpdate.setString(1, "Apple");
PSUpdate.setString(2, DBID);
PSUpdate.executeUpdate();
dbConnection.commit();
}
if(DBChoice.equals("delete")){
//for delete
PSDelete = dbConnection.prepareStatement(deleteTable);
PSDelete.setString(1, DBID);
PSDelete.executeUpdate();
dbConnection.commit();
}
if(DBChoice.equals("statement")){
//for statement
PSStatement = dbConnection.prepareStatement(statementTable);
PSStatement.executeUpdate();
dbConnection.commit();
}
System.out.println("Success!");
}catch(SQLException e){
System.out.println("Error occured " + e.toString());
dbConnection.rollback();
}
finally{
if(PSInsert !=null){
PSInsert.close();
}
if(PSUpdate != null){
PSUpdate.close();
}
if(dbConnection != null){
dbConnection.close();
}
}
}
private static Connection getDBConnection(){
Connection con = null;
try{
Class.forName("com.mysql.jdbc.Driver");
}catch(ClassNotFoundException e){
System.out.println("Error 1 : " + e.getMessage());
}
try{
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/bongbong","root","");
}catch(SQLException e){
System.out.println("Error 2 : " + e.getMessage());
}
return con;
}
}
Oh by the way,
The values of the variables are currently temporary. I'll be changing them later to whatever they're going to be catching. I'm referring to these:
String DBTable = "fruits";
String DBColumnSet = "fruit";
String DBID = "23";
String DBColumnSingle = "fruit";
String DBChoice ="insert";
String DBCS = "";
and this
String statementTable = "INSERT INTO fruits(fruit) VALUES('grapes')";
Problem
What I really need help from is with the ? thing from the first code I posted after the VALUES word inside the String insertTable. I want it so that when I place a column amount in my interface then it'll add more ? in accordance with what was inputted (I also want it to add another PSInsert.setString(n, "value") with n+1 in accordance with the column amount inputted if it's possible). Can anyone tell me how to? I'm really new to java and I'm still a student studying at his best.
I want it to add those ? thing because what if I add more columns or if I use another table with more columns other than my fruits table. (I want it so that whatever I'm going to place in DBColumnSet --with columns separated by comma --will also relate to how many ? are going to be placed).
Oh by the way, it's a general api so I can't provide an interface.

MySQL - checking if column exists if not create it in Java

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
}

setString for prepared statement not working

I am trying to use the setString(index, parameter) method for Prepared Statements in order to create a ResultSet but it doesn't seem to be inserting properly. I know the query is correct because I use the same one (minus the need for the setString) in a later else. Here is the code I currently have:
**From what I understand, the ps.setString(1, "'%" + committeeCode + "%'"); is supposed to replace the ? in the query but my output says otherwise. Any help is appreciated.
public String getUpcomingEvents(String committeeCode) throws SQLException{
Context ctx = null;
DataSource ds = null;
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
StringBuilder htmlBuilder = new StringBuilder();
String html = "";
try {
ctx = new InitialContext();
ds = (DataSource) ctx.lookup("java:ConnectDaily");
conn = ds.getConnection();
if(committeeCode != null){
//get all events
String queryStatement = "SELECT " +
.......
"WHERE c.calendar_id = ci.calendar_id AND c.short_name LIKE ? " +
"AND ci.style_id = 0 " +
"AND ci.starting_date > to_char(sysdate-1, 'J') " +
"AND ci.item_type_id = cit.item_type_id " +
"ORDER BY to_date(to_char(ci.starting_date), 'J')";
ps = conn.prepareStatement(queryStatement);
ps.setString(1, "'%" + committeeCode + "%'");
System.out.println(queryStatement);
rs = ps.executeQuery();
if (rs != null){
while(rs.next()){
String com = rs.getString("name");
String comID = rs.getString("short_name");
String startTime = rs.getString("starting_time");
String endTime = rs.getString("ending_time");
String name = rs.getString("contact_name");
String desc = rs.getString("description");
String info = rs.getString("contact_info");
String date = rs.getString("directory");
htmlBuilder.append("<li><a href='?com="+committeeCode+"&directory=2014-09-10'>"+com+" - "+ date +" - "+startTime+" - "+endTime+"</a> <!-- Link/title/date/start-end time --><br>");
htmlBuilder.append("<strong>Location: </strong>"+comID+"<br>");
htmlBuilder.append("<strong>Dial-In:</strong>"+com+"<br>");
htmlBuilder.append("<strong>Part. Code:</strong>"+info+"<br>");
htmlBuilder.append("<a href='http://nyiso.webex.com'>Take me to WebEx</a>");
htmlBuilder.append("</li>");
}
}
html = htmlBuilder.toString();
.
.
.
}catch (NamingException e) {
e.printStackTrace();
//log error and send error email
} catch (SQLException e) {
e.printStackTrace();
//log error and send error email
}finally{
//close all resources here
ps.close();
rs.close();
conn.close();
}
return html;
}
}
Output
14:18:22,979 INFO [STDOUT] SELECT to_char(to_date(to_char(ci.starting_date), 'J'),'mm/dd/yyyy') as start_date, to_char(to_date(to_char(ci.ending_date), 'J'),'mm/dd/yyyy') as end_date, to_char(to_date(to_char(ci.starting_date), 'J'),'yyyy-mm-dd') as directory, ci.starting_time, ci.ending_time, ci.description, cit.description as location, c.name, c.short_name, ci.add_info_url, ci.contact_name, ci.contact_info FROM calitem ci, calendar c, calitemtypes cit WHERE c.calendar_id = ci.calendar_id AND c.short_name LIKE ? AND ci.style_id = 0 AND ci.starting_date > to_char(sysdate-1, 'J') AND ci.item_type_id = cit.item_type_id ORDER BY to_date(to_char(ci.starting_date), 'J')
There is no need for the quotes in setString:
ps.setString(1, "%" + committeeCode + "%");
This method will bind the specified String to the first parameter. It will not change the original query String saved in queryStatement.
The placeholder remains as part of the SQL text.
The bind value is passed when the statement is executed; the actual SQL text is not modified. (This is one of the big advantages of prepared statements: the same exact SQL text is reused, and we avoid the overhead of a hard parse.
Also note that you are including single quotes within the value, which is a bit odd.
If the bind placeholder were to be replaced in the SQL text, assuming committeeCode contains foo, the equivalent SQL text would be:
AND c.short_name LIKE '''%foo%'''
which will match only c.short_name values that begin and end with a single quote, and contain the string foo.
(This looks more like Oracle SQL syntax than it does MySQL.)
As we know that in setString we can pass string value only, So even if we write the code like this:
String param="'%"+committeeCode+"%'";
And if you print the value of param it will throw error, Hence you cannot use it as well in prepared statement.
You need to modify modify it little bit as:
String param="%"+committeeCode+"%";(Simpler one, other way can be used)
ps.setString(1,param);

Categories