Working with database in java requires a set of lines of code to be written. If the database connection is established again-n-again the same set of lines need to be repeated again-n-again which creates an overhead to programmer.
Hence, I am making a utility class which works as a database agent. The method of this class will be responsible for all the database related stuff from establishing a connection to executing a query and returning the result.
My class goes like this -
package connect;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class Connector
{
private ResultSet rs;
public String password;
public void connect(String dbUrl, String dbClass, String dbUserName, String dbPassword, String query)
{
try
{
Class.forName(this.dbClass).newInstance();
Connection conn = DriverManager.getConnection (dbUrl, dbUserName, dbPassword);
Statement stmt = conn.createStatement();
rs = stmt.executeQuery(query);
while(rs.next())
{
this.password = (String) rs.getObject(1);
}
conn.close();
} //end try
catch(Exception e)
{
e.printStackTrace();
}
}
}
When a programmer needs a query to be executed, below lines can be written for that purpose -
Connector con = new Connector();
con.connect("your database URL",
"your database driver",
"your database username",
"your database password",
"your query");
My question here is that right now I am retreiving the data from ResultSet in the connect method itself. The retreived data can vary from query-to-query hence I want a data structure in which I can store the data from rs in connect() method and return it.
It will look like -
<data structure> result = con.connect("your database URL",
"your database driver",
"your database username",
"your database password",
"your query");
Can someone please suggest me a data structure for this purpose?
Not sure why you're re-inventing this particular wheel, and it seems like if you're repeatedly connecting for every query you're basically writing yourself out of any performance, but why not just use RowSetDynaClass if you're not going to map to objects?
Just be aware that there are a ton of pre-existing solutions that have been tested and proven. And use connection pools; creating connections is expensive.
There is a lot of Open Source, implementation like the one you want to do above.
Spring JDBCTemplate for example.
If you can't use any libraries already available to solve your problem, you could use
Vector<Map<String, Object>>
Where String contains column name and Object is the value of the column. Each element in the Vector corresponds to a row of the ResultSet. This may not be the most efficient data structure but should get the job done.
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed last month.
Improve this question
I am working on a JavaFX project that connects to a MySQL database (and as such I use a JDBC [java.sql] to perform various tasks from java to SQL). I've recently come into a lot of confusion regarding the standard procedure of connecting to a database.
From what I understand, I should create one instance of a Connection object, then I should pass this instance around the project until the end of the entire program.
Connection connection = DriverManager.getConnection(url, username, password);
If the statement above is true then I only need to call the previous line once in the entire program and pass the connection variable around the entire program whenever I need to connect to the database. If this is true, how can I pass around this instance of connection from one controller class to another?
Furthermore, I am seeing that it is a standard procedure (in order to maximize GUI responsiveness) to push all "costly" procedures onto another thread (not the main one that is handling the GUI) using the javafx.concurrencies library. If this statement is true, is it also true that I should push all methods that interact with the database into say a Task<V> class?
Up until now, I created multiple object classes that each communicate with the database. In addition, I created a Database class that allows me to initialize a "new" connection to the database. For example,
public class Database {
public static Connection createConnectionToDB() throws SQLException{
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
}
}
Now in the various object classes, when I need to add, update, or delete some data from the database, I created an add, update, and delete method in each object to handle that object's data in the database. However, for each method, I thought (from my natural naivete) that it would be standard to first create a connection to the database via the previous Connection connection = createConnectionToDB() method in the Database class then to use this connection instance to do what needs to be done, then close the connection with the connection.close() method.
For example,
public class Object{
add(){
Connection connection = Database.createConnectionToDB();
PreparedStatement statement = ...
// some other lines
connection.close();
}
update(){
Connection connection = Database.createConnectionToDB();
PreparedStatement statement = ...
// some other lines
connection.close();
}
delete(){
Connection connection = Database.createConnectionToDB();
PreparedStatement statement = ...
// some other lines
connection.close();
}
}
That is, for each object, and each method for each object, a new connection is created and closed. Is this bad practice?
It could be a bad practice if your application creates new database Connection objects with longer scopes every time it needs to perform a query operation on the database. I would suggest that you should put the Connection initiation code into a try-with-resources code block so that for each database operation, a connection is established, query operation is performed and the connection is closed within the block. The class to fetch new Connection could look like this:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DatabaseConnection {
private static final String DB_URL = "jdbc:mysql://localhost/dbName";
private static final String USER = "dbUser";
private static final String PASS = "dbPassword";
public static Connection getDatabaseConnection()
{
Connection databaseConnection;
try {
databaseConnection = DriverManager.getConnection(DB_URL, USER, PASS);
} catch (SQLException e)
{
e.printStackTrace();
}
return databaseConnection;
}
}
So, you can make a call to the static method DatabaseConnection.getDatabaseConnection() from any point (controller or other class) of your application to obtain the new Connection instance and use it.
And yes, it would be better to make use of the javafx.concurrency library in case of a JavaFX application. You can utilize the Task<V> class like:
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
String query = "SELECT id, first, last, age FROM Employees";
try(Connection conn = DatabaseConnection.getDatabaseConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query);) {
// Extract data from result set
while (rs.next()) {
// Retrieve by column name
System.out.print("ID: " + rs.getInt("id"));
System.out.print(", Age: " + rs.getInt("age"));
System.out.print(", First: " + rs.getString("first"));
System.out.println(", Last: " + rs.getString("last"));
}
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
};
And invoking the Task by:
new Thread(task).start();
Note that you should populate all the ResultSet data into your own custom non-jdbc classes so that the ResultSet class can also be disposed within the block. Furthermore, you should also consider applying Transaction Management to your code containing Database Operations (in case your application is concurrently making many database calls from different points/classes) for data consistency.
I am trying to access a database from a different server , but no success so far. The event is, when I choose the object "ALL" in my combobox, the table will load all data from different servers.
The current code, which I only connected to the localhost, works fine. However, when I try to connect another server to load both of their data, I get a syntax error when trying to put 192.168.1.51.sales.items in the String sqlall. Also, I tried modifying the prepareStatement by writing cn.prepareStatement(sqlall) + cn1.prepareSatement("union Select * from 192.168.1.52.sales.items); I have no more idea on how to connect on both servers.
I would like to apologize beforehand if you find my coding a bit messy. Thank you. My code is as follows:
private void combobox_branchItemStateChanged(java.awt.event.ItemEvent evt) {
Object branch = combobox_branch.getSelectedItem();
try
{
// All is selected
if("All".equals(branch))
{
Connection cn = db.itemconnector.getConnection();
String sqlall = " Select * from sales2.items union Select * from sales1.items union Select * from sales.items " ; //I tried accessing multiple databases in my own localhost and worked.
PreparedStatement ps = cn.prepareStatement(sqlall);
ResultSet rs = ps.executeQuery();
DefaultTableModel tm = (DefaultTableModel)itemTable.getModel();
tm.setRowCount(0);
while(rs.next())
{
Object o[] = {rs.getInt("id"), rs.getString("location"), rs.getString("product_name"),rs.getString("product_category"),rs.getString("product_description"),rs.getInt("product_stock"), rs.getFloat("product_price"), rs.getString("product_status")};
tm.addRow(o);
}
}
catch (Exception e)
{
JOptionPane.showMessageDialog(null, e, "Connection Error", JOptionPane.ERROR_MESSAGE);
}
}
And I have a class in a different package and this is its code:
package db;
import java.sql.*;
public class itemconnector {
public static Connection getConnection() throws Exception
{
Class.forName("com.mysql.jdbc.Driver");
Connection cn = (Connection)
DriverManager.getConnection("jdbc:mysql://192.168.1.50:3306/sales","root","");
return cn;
}
It is not possible to query different databases on different servers in a single SQL query.
It is also not possible to get around this by "concatenating" prepared statements. (For a start, that is nonsensical Java!)
You need to open a separate Connection to each separate database server and query the relevant tables on that server. Then combine the information from the separate ResultSet objects in Java code.
The "combining" will be something like iterating the results in each result set and adding them to a Java data structure ...
I am trying to solve this problem with the help of properties file, but in a Properties file, we can handle only Database Driver problem. If I want to switch my MySQL to Oracle database I need to change my all query. The problem is how to make query independent in JDBC?
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
public class DBIndependencyExample {
public static void main(String[] args) {
try {
Properties pros = new Properties();
InputStream fis = new FileInputStream(
"D:\\Programs\\Eclipse\\DBIndependecyByPropertiesFile\\src\\connectdb.properties");
pros.load(fis);
String Drivername = pros.getProperty("k1");
//System.out.println(Drivername);
String url = pros.getProperty("k2");
String un = pros.getProperty("k3");
String pw = pros.getProperty("k4");
Class.forName(Drivername);
Connection con = DriverManager.getConnection(url, un, pw);
System.out.println("Driver Is Loaded With" + Drivername);
System.out.println("Connection is Opened");
Statement smt = con.createStatement();
String sql = pros.getProperty("k5");
//System.out.println(sql);
ResultSet rs = smt.executeQuery(sql);
while (rs.next()) {
System.out.println("username is:" + rs.getString(1) + " password is:" + rs.getString(2));
}
con.close();
System.out.println("Connection is closed");
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Properties File:
//Mysql Connectivity
//Start Properties File Code
k1=com.mysql.jdbc.Driver
k2=jdbc:mysql://localhost:3306/practice
k3=root
k4=root
k5=select * from student
//Oracle Connectivity
k1=oracle.jdbc.driver.OracleDriver
k2=jdbc:oracle:thin:#localhost:1521/orcla
k3=scott
k4=manish
k5=select * from dept
If i want to switch my mysql to oracle database i need to change my
all query.
If your SQL queries rely only on the ANSI SQL and never on proprietary specificites (function, keywords, and...) you should be able to switch from an DBMS to another one without any change in the queries.
Note that Hibernate will not translate for a DBMS specifities to another one as for example translate a query on the DUAL table written in Oracle to a MySQL way.
Hibernate ensures that your SQL queries be portable while you don't create native queries, a possibility still provided by Hibernate.
Here is the original SQL ANSI draft and here a download link for the last version of Information technology -- Database languages -- SQL -- Part 1: Framework (SQL/Framework)
I don't think so if you switch to hibernate or any orm frameworks. In case of hibernate you can use hql to write the queries in that way you are database independent no matter what database you switch hibernate will take care. You can even look at Spring CrudRepository. I highly doubtful you can achieve database independent queries just with the JDBC. It is good to begin with JDBC for a long run you should consider orm frameworks.
You can write a generic method that can handle any sql query (regardless of the database used). The trick here is to use database metadata to automatically
populate a hash map (a hash map is convenient because we can use the column names as keys) containing the query results. For parameterized queries, you'll need a more complex method, which can be found at the link provided below (DBSelect.getRecordsForCustomQuery). If all your queries targeting different databases are the same, then you don't even have to use metadata, just hardcode the column names.
public Map<String, Object> getQueryResults(String query, Connection conn){
ResultSet rs=null;
PreparedStatement stmt=null;
Map<String, Object> objMap = new HashMap<>();
stmt = conn.prepareStatement(sqlQuery);
rs = stmt.executeQuery();
while(rs.next()){
for (int i=1;i<=rs.getMetaData().getColumnCount();i++) {
objMap.put( rs.getMetaData().getColumnName(i), rs.getObject(i));
}
}
return mapObj;
}
You can, of course, modify this method to suit your needs.
A slightly more elaborate solution which uses reflection and generics can be found here ( https://github.com/IvanGH2/EasyORM ) but you don't
even have to use all of that.
I want to fetch parameter name and parameter type of given prepared statement. I am using MySQL Database. But when I run my program it is throwing an error:
Exception in thread "main" java.sql.SQLException: Parameter metadata not available for the given statement
at this line
String paramTypeName = paramMetaData.getParameterTypeName(param);
I don't know why this is happening. Please anybody help me if possible.
Here's my code:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.Statement;
public class Main {
public static void main(String[] args) throws Exception {
Connection conn = getMySqlConnection();
Statement st = conn.createStatement();
String query = "select * from survey where id > ? and name = ?";
PreparedStatement pstmt = conn.prepareStatement(query);
ParameterMetaData paramMetaData = pstmt.getParameterMetaData();
if (paramMetaData == null) {
System.out.println("db vendor does NOT support ParameterMetaData");
} else {
System.out.println("db vendor supports ParameterMetaData");
// find out the number of dynamic parameters
int paramCount = paramMetaData.getParameterCount();
System.out.println("paramCount=" + paramCount);
System.out.println("-------------------");
for (int param = 1; param <= paramCount; param++) {
System.out.println("param number=" + param);
String paramTypeName = paramMetaData.getParameterTypeName(param);
System.out.println("param SQL type name=" + paramTypeName);
}
}
pstmt.close();
conn.close();
}
public static Connection getMySqlConnection() throws Exception {
String driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "";
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, username, password);
return conn;
}
}
According to this
Should the driver generate simplified parameter metadata for PreparedStatements when no
metadata is available either because the server couldn't support preparing the statement, or
server-side prepared statements are disabled?
You have to set generateSimpleParameterMetadata to true
use a connection string similar to this
jdbc:mysql://localhost:3306/mydb?generateSimpleParameterMetadata=true
MySQL JDBC driver currently does not support it. I am solving the similar issue and came up with the following workaround:
include H2 database in your project (it can also run in embedded mode or in-memory)
translate your MySQL create database script to H2 syntax (or write it in ANSI so it is compatible with both)
compile prepared statements on H2 database first and get metadata from them - H2 database supports this function and SQL query syntax is similar in most cases - then save the obtained meta information for later use
there might be differences in data types, etc, but in general this should give you about 80% match with MySQL without too much hassle
I know it has much caveats, but it might work in some use cases.
Also consider upgrading MySQL database to 5.7, there are some enhancements related to prepared statements which may help, but I am not very deeply knowledgable about those:
http://dev.mysql.com/doc/refman/5.7/en/prepared-statements-instances-table.html
http://dev.mysql.com/doc/refman/5.7/en/prepare.html
You have not set the parameter to the prepared statements, without which you cannot get parameter metadata. so first set the parameter
pstmt.setInt(val)
pstmt.setString(val)
After adding the parameters you can get the meta data about the parameter.
Hope this helps.
can anyone please help me on this? I am trying to execute a SQL query (to Microsoft SQL) using JAVA. But, nothing happens on my table. Also, no exception either. But i am pretty sure that this code directly connecting to my DB. Here's my code.
package Testing;
//import java.sql.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
public class TestingClass {
public static void main(String[] srg)
{
String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; // Start JDBC
String dbURL = "jdbc:sqlserver://localhost:1433;DatabaseName=OPERATIONS"; // Connect the server and the database
String userName="AppControlTeam";
String userPwd="*****";
//
Connection connection = null;
try{
Class.forName(driverName);
connection = DriverManager.getConnection(dbURL,userName,userPwd);
String sql = "DELETE FROM GFR_GROWTH_RATE";
PreparedStatement statement = connection.prepareStatement(sql);
statement.executeUpdate();
connection.close();
}
catch (Exception e){
e.printStackTrace();
}
}
}
I have been already added the JDBC driver on the package. Also, i'm pretty sure that this is not a classpath issue since the connection to DB thru this code is already success.
Thanks in advance to someone who can help! :D
-Jiro
Your DELETE may be rolled back if you didn't set your JDBC driver's autocommit property to true. Maybe try calling
connection.commit();
// right before...
connection.close();
Or alternatively:
connection.setAutoCommit(true);
// before...
PreparedStatement statement = connection.prepareStatement(sql);
Probably the row is locked by a different session, may be an uncommitted session that has modified (deleted/updated) the records from the table. Another possibility is by a tool which open the row/record with locked state - so the delete statement hangs.