So, I am creating an application in javaFX (for learning purposes only) which mainly consists of a tabpane with sub-fxmls for each tab. Each tab consist of a tableview with data collected from a database. To connect to the database I have a separate connection class that creates a new connection pool using c3p0.
My first question is: Should I create (and close) a new connection for every query I make to the database?
Example: upon entering a new tab I create a new connection, collect the data and then close the connection (with statement and resultset).
If not, how would I do this in a proper and efficient way?
My second question: Currently my application is doing what's described above, but I notice that if I leave the application open for a while, without interacting with it, whenever I start interacting with it again it freezes for a bit before it continues. The current table that is viewed at that time then shows "no data in table", until I switch to another tab and then the data is displayed properly again. Why does this happen?
I am quite new to Java in general, and I couldn't find anything by searching that resembled my problem. My database is a postgres db.
EDIT 1:
Regarding question 2:
I have followed the mvc-pattern for this, not sure if that's a good idea, but that's another question.
There are four classes that play a part in a table's creation, which are:
Controller class:
public class StockController {
//tableview from fxml-file
#FXML
private TableView<List<Object>> stockTable;
//
private BuildDataModel stockData = new BuildDataModel();
//Called from main whenever currently active tab changes to this tab.
public void CreateView(){
String query = "SELECT component.name, stock.* FROM component NATURAL JOIN stock";
stockData.BuildData(stockTable, query);
}
//Called from main whenever currently active tab changes to another tab.
public void DestroyView(){
if(!stockTable.getColumns().isEmpty()) {
stockData.DeleteData();
}
}
}
Model class:
public class BuildDataModel {
private Connection conn;
#FXML
private TableView<List<Object>> tableview;
public TableData data;
private TableData getAllData(String query){
List<List<Object>> data = new ArrayList<>();
List<String> columnNames = new ArrayList<>();
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = PostgresConnection.CreateConnection();
st = conn.prepareStatement(query);
rs = st.executeQuery();
//We create columns
int columnCount = rs.getMetaData().getColumnCount();
for(int i = 1; i <= columnCount; i++) {
String columnName = rs.getMetaData().getColumnName(i);
columnNames.add(columnName);
}
//We populate our tabledata object with the data
while(rs.next()){
List<Object> row = new ArrayList<>();
for(int i = 1; i <= columnCount; i++){
row.add(rs.getObject(i));
}
data.add(row);
}
}
catch (Exception e){
e.printStackTrace();
}
finally {
DbUtils.closeQuietly(conn, st, rs);
}
return new TableData(columnNames, data);
}
//Called from controller class, calls getAllData with the specified query
//and populates tableview based on this.
public void BuildData(TableView<List<Object>> tableview, String query) {
this.tableview = tableview;
try {
data = getAllData(query);
for (int i = 0 ; i < data.getNumColumns(); i++) {
TableColumn<List<Object>, Object> column = new TableColumn<>(data.getColumnName(i));
int columnIndex = i;
column.setCellValueFactory(cellData ->
new SimpleObjectProperty<>(cellData.getValue().get(columnIndex)));
tableview.getColumns().add(column);
}
tableview.getItems().setAll(data.getData());
}
catch (Exception e) {
}
}
public void DeleteData(){
tableview.getColumns().clear();
}}
Connection class:
public class PostgresConnection {
//DB credentials, modify as needed
private static final String HOST = "localhost";
private static final String DATABASE = "<INSERT DATABASE HERE>";
private static final String USERNAME = "<INSERT USERNAME HERE>";
private static final String PASSWORD = "<INSERT PASSWORD HERE>";
//Default postgresql port, don't change unless you know what you're doing.
private static final String PORT = "5432";
private static final String URL = "jdbc:postgresql://" + HOST + ":" + PORT + "/" + DATABASE;
private static final String DRIVER_NAME = "org.postgresql.Driver";
private static Connection conn;
//Disables C3P0-pool logging
static {
Properties p = new Properties(System.getProperties());
p.put("com.mchange.v2.log.MLog", "com.mchange.v2.log.FallbackMLog");
p.put("com.mchange.v2.log.FallbackMLog.DEFAULT_CUTOFF_LEVEL", "OFF");
System.setProperties(p);
}
private static ComboPooledDataSource cpds = new ComboPooledDataSource();
public static Connection CreateConnection() throws SQLException {
try {
cpds.setDriverClass(DRIVER_NAME);
} catch (PropertyVetoException e) {
e.printStackTrace();
}
cpds.setJdbcUrl(URL);
cpds.setUser(USERNAME);
cpds.setPassword(PASSWORD);
cpds.setMinPoolSize(3);
cpds.setAcquireIncrement(5);
cpds.setMaxPoolSize(100);
cpds.setMaxStatements(180);
return cpds.getConnection();
}}
and DAO:
public class TableData {
private final List<String> columnNames;
private final List<List<Object>> data;
public TableData(List<String> columnNames, List<List<Object>> data) {
this.columnNames = columnNames;
this.data = data;
}
public int getNumColumns() {
return columnNames.size();
}
public String getColumnName(int index) {
return columnNames.get(index);
}
public int getNumRows() {
return data.size();
}
public Object getData(int column, int row) {
return data.get(row).get(column);
}
public List<List<Object>> getData() {
return data;
}
public void removeData(){
data.clear();
columnNames.clear();
}}
Related
Let me clarify the question.
I'm creating a JavaFX application that has 2 ComboBoxes. One displays available catalogs in MySQL DB and other shows available tables in the selected catalog from first ComboBox. Now the problem is that I need to create a TableView that would display the results of "desc tableName" MySQL command.
I'm using jdbc API, executing "desc ${tableName}" using Statement interface and getting the data into a ResultSet. I'm able to fill the TableView column names with column names from ResultSetMetaData object.
Now, I know that in order to fill the data cells in the TableView, we need to use a pojo class that would define the data model. But, by now you can understand that since the tables are being selected dynamically by user, I cannot create at least a generic data model class for TableView.
I also know that we cannot set data in individual cells of the TableView programmatically.
So, I have instead used a HashMap as data model for the TableView and used the keys of HashMap as the CellValueFactory properties.
TableView<HashMap<String, String>> tableView;
ObservableList<TableColumn<HashMap<String, String>, String>> tvcolumns = FXCollections.observableArrayList();
tableColumns.forEach((t) -> {
tvcolumns.add(new TableColumn<>(t));
});
for (int i = 0; i < tvcolumns.size(); i++) {
TableColumn<HashMap<String, String>, String> temp = tvcolumns.get(i);
temp.setCellValueFactory(new PropertyValueFactory<>(keySet.get(i)));
}
tableView.getItems().clear();
tableView.setItems(tableData);
tableView.getColumns().addAll(tvcolumns);
Now this HashMaps's keys contain columns names of the ResultSet data and values contains data of the corresponding columns in the ResultSet. I've written the code in such a way that TableView takes in an ObservableList<> of these HashMap objects (one HashMap object for each row in ResultSet) and populate the cells in TableView with HashMap's values, based on HashMap's keys.
But the data is not being shown in the TableView. I don't even know if something like this is possible.
Here is the full code:
--> MainUiController.java :
public class MainUiController implements Initializable {
#FXML
private AnchorPane rootPane;
#FXML
private VBox vBoxMain;
#FXML
private HBox hBoxTop;
#FXML
private VBox vBoxTopLeft;
#FXML
private ComboBox<String> cbCatalog;
#FXML
private VBox vBoxTopRight;
#FXML
private ComboBox<String> cbTables;
#FXML
private HBox hBoxBottom;
#FXML
private TitledPane titledPaneBottom;
#FXML
private AnchorPane anchorPaneBottom;
#FXML
private TableView<HashMap<String, String>> tableView;
DBHelper dbHelper = new DBHelper();
/**
* Initializes the controller class.
*
* #param url
* #param rb
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
ObservableList<String> catalogItems = cbCatalog.getItems();
catalogItems.clear();
catalogItems.addAll(dbHelper.getCatalogs());
titledPaneBottom.setExpanded(false);
}
#FXML
private void populateTables(ActionEvent event) {
String dbName = ((ComboBox<String>) event.getSource()).getValue();
System.out.println("catalog value: " + dbName);
dbHelper.useDB(dbName);
ObservableList<String> tables = cbTables.getItems();
tables.clear();
tables.addAll(dbHelper.getTables());
}
#FXML
private void descTable(ActionEvent event) {
String tableName = ((ComboBox<String>) event.getSource()).getValue();
ObservableList<String> tableColumns = dbHelper.getTableColumns(tableName);
ObservableList<HashMap<String, String>> tableData = dbHelper.getTableData();
List<String> keySet = null;
if (!tableData.isEmpty()) {
keySet = new ArrayList<>(tableData.get(0).keySet());
}
titledPaneBottom.setText("\"" + tableName + "\" description:");
ObservableList<TableColumn<HashMap<String, String>, String>> tvcolumns = FXCollections.observableArrayList();
tableColumns.forEach((t) -> {
tvcolumns.add(new TableColumn<>(t));
});
for (int i = 0; i < tvcolumns.size(); i++) {
TableColumn<HashMap<String, String>, String> temp = tvcolumns.get(i);
temp.setCellValueFactory(new PropertyValueFactory<>(keySet.get(i)));
}
tableView.getItems().clear();
tableView.setItems(tableData);
tableView.getColumns().addAll(tvcolumns);
}
}
--> DBHelper.java:
class DBHelper {
Connection con;
Statement st;
ObservableList<String> tablesList = FXCollections.observableArrayList();
ObservableList<String> columnList = FXCollections.observableArrayList();
HashMap<String, String> tableData;
ObservableList<HashMap<String, String>> tableDataList = FXCollections.observableArrayList();
String dbName, tableName;
ResultSet tableCols;
ResultSetMetaData colsMetaData;
public DBHelper() {
try {
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/", "*****", "******");
st = con.createStatement();
} catch (SQLException ex) {
Logger.getLogger(DBHelper.class.getName()).log(Level.SEVERE, null, ex);
}
}
public ObservableList<String> getCatalogs() {
try {
ObservableList<String> catalogList = FXCollections.observableArrayList();
DatabaseMetaData dbmd = con.getMetaData();
ResultSet catalogs = dbmd.getCatalogs();
while (catalogs.next()) {
catalogList.add(catalogs.getString(1));
}
return catalogList;
} catch (SQLException ex) {
Logger.getLogger(DBHelper.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public ObservableList<String> getTables() {
try {
ResultSet tables = st.executeQuery("show tables");
tablesList.clear();
while (tables.next()) {
tablesList.add(tables.getString(1));
}
return tablesList;
} catch (SQLException ex) {
Logger.getLogger(DBHelper.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
void useDB(String dbName) {
this.dbName = dbName;
try {
int execute = st.executeUpdate("use " + dbName);
} catch (SQLException ex) {
Logger.getLogger(DBHelper.class.getName()).log(Level.SEVERE, null, ex);
}
}
public ObservableList<String> getTableColumns(String tableName) {
this.tableName = tableName;
try {
tableCols = st.executeQuery("desc " + tableName);
colsMetaData = tableCols.getMetaData();
columnList.clear();
int count = 1;
while (count <= colsMetaData.getColumnCount()) {
columnList.add(colsMetaData.getColumnName(count));
count++;
}
return columnList;
} catch (SQLException ex) {
Logger.getLogger(DBHelper.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public ObservableList<HashMap<String, String>> getTableData() {
tableDataList.clear();
if (tableCols != null & colsMetaData != null) {
try {
while (tableCols.next()) {
tableData = new HashMap<>();
int count = 1;
while (count <= colsMetaData.getColumnCount()) {
tableData.put(colsMetaData.getColumnName(count), tableCols.getString(count));
count++;
}
tableDataList.add(tableData);
}
tableCols.close();
tableCols = null;
colsMetaData = null;
return tableDataList;
} catch (SQLException ex) {
Logger.getLogger(DBHelper.class.getName()).log(Level.SEVERE, null, ex);
}
}
return null;
}
}
Please help me on solving this issue. I'm pretty sure that using HashMap methods as datamodel for a TableView is wrong/not possible. But I do not know how to create a generic datamodel object in this case.
Please help solve this problem.
I'm currently learning JavaFx and I wanted to make this application as a challenge. Any insight on this would be highly helpful for my career.
This may be helpful. I use a custom TableView that accepts a ResultSet for the data.
It's a bit of a hack implementation, but it works for me in these cases. I am not sure that keeping a ResultSet open outside of the datasource is a wise decision, but it may lead you in the right direction at least:
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.util.Callback;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class ResultSetTableView extends TableView {
private ResultSet resultSet;
private List<String> columnNames = new ArrayList<>();
public ResultSetTableView(ResultSet resultSet) throws SQLException {
super();
this.resultSet = resultSet;
buildData();
}
private void buildData() throws SQLException {
ObservableList<ObservableList> data = FXCollections.observableArrayList();
for (int i = 0; i < resultSet.getMetaData().getColumnCount(); i++) {
final int j = i;
TableColumn col = new TableColumn(resultSet.getMetaData().getColumnName(i + 1));
col.setCellValueFactory((Callback<TableColumn.CellDataFeatures<ObservableList, String>, ObservableValue<String>>) param -> {
if (param.getValue().get(j) != null) {
return new SimpleStringProperty(param.getValue().get(j).toString());
} else {
return null;
}
});
getColumns().addAll(col);
this.columnNames.add(col.getText());
}
while (resultSet.next()) {
//Iterate Row
ObservableList<String> row = FXCollections.observableArrayList();
for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
//Iterate Column
row.add(resultSet.getString(i));
}
data.add(row);
}
//FINALLY ADDED TO TableView
setItems(data);
}
public List<String> getColumnNames() {
return columnNames;
}
}
My library method signature:
TableViewUtils.createColumnsAndFillTableView(
TableView<List<Object>> tableView,
ResultSet resultSet,
boolean calculatePrefHeight,
StringProperty filterProperty)
as filterProperty you can pass TextField textProperty to filter items by user input
I'm reading data from a DB and like to appended each row of data from the DB using a Callback. I've managed to get the Callback working but I don't know how I will get it append the data to file. Here is my code.
Main
public class Main {
public static void main(String[] args) {
FileIO fileIO = new FileIO();
fileIO.writeRStoFile();
}
}
FileIO
public class FileIO implements DBAccess.CallBack {
public void writeRStoFile() {
DBAccess dbAccess = new DBAccess(this);
String fileName = "/result.csv";
try (FileWriter fw = new FileWriter(fileName)) {
System.out.println("Starting data download from DB...");
dbAccess.readDB();
// HERE I LIKE TO APPEND EACH ROW TO THE FILE
fw.append('\n');
System.out.println("Finished data download from DB...");
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void iAmDone(String row) {
System.out.println("Row: " + row);
}
}
DBAccess
public class DBAccess {
public interface CallBack {
public void iAmDone(String row);
}
private final CallBack callBack;
public DBAccess(CallBack callBack) {
this.callBack = callBack;
}
public void readDB() {
String url = "jdbc:Cobol:////Dev/Project Files/DatAndCpyFiles";
try (Connection con = DriverManager.getConnection(url, "", "");
Statement stmt = con.createStatement())
{
stmt.setFetchSize(10);
Class.forName("com.hxtt.sql.cobol.CobolDriver").newInstance();
String sql = "select * from PROFS";
ResultSet rs = stmt.executeQuery(sql);
ResultSetMetaData resultSetMetaData = rs.getMetaData();
int iNumCols = resultSetMetaData.getColumnCount();
for (int i = 1; i <= iNumCols; i++) {
callBack.iAmDone(resultSetMetaData.getColumnLabel(i) + ";");
}
String field;
while (rs.next()) {
String row = "";
for (int i = 1; i <= iNumCols; i++) {
field = rs.getString(i);
field = field.trim() + ";";
row = row + field;
}
callBack.iAmDone(row);
}
rs.close();
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
I'm not sure how I get the data from iAmDone() into the writeRStoFile() method. I'm able to print the data to the console.
One way that I can think of is to declare fw as a member variable in FileIO. That way you can call fw.append(...) in iAmDone. (You might have to change the try-with-resources then.)
I have a question about getColumnName () methods. I am using it in the UCanAccess library. I'm trying to get column names and put it at table exactly as they are in MS Access DB. But I get random order. Mayby somebody know what do I incorrect?
For the test I had delete adding to the table and implemented just printing.
Code is:
public class Connections {
private final String dbUrl="jdbc:ucanaccess://C:/Users/Admin/Desktop/DB.accdb";
private final String user="";
private final String password="";
public Statement conecting(){
Statement stat=null;
try{
Connection connection=DriverManager.getConnection(dbUrl,user,password);
stat=connection.createStatement();
}
catch(SQLException exc){
exc.printStackTrace();
}
return stat;
}
public String[] prepearedNamesOfColumns(String nameOfTable, Statement stat) throws SQLException{
String[] result=null;
ResultSet res=stat.executeQuery("select*from "+nameOfTable);
ResultSetMetaData rsmd=res.getMetaData();
int columnCount=rsmd.getColumnCount();
for(int i=1; i<=columnCount;i++){
System.out.println(rsmd.getColumnName(i));
}
return result;
}
Main class:
public class Demonstration {
public static void main(String[] args) throws SQLException {
Connections operacje= new Connections();
String[] tab=new String[6];
tab[0]="dluznicy";
tab[1]="Filipczak";
tab[2]="Karol";
tab[3]="PoznaĆ";
tab[4]="M";
tab[5]="34";
String[] wynik=operacje.prepearedNamesOfColumns(tab[0], operacje.conecting());
}
}
At Access I have order like:
1) id_dluznik
2) nazwisko
3) Imie
4) miejsce_urodzenia
5) plec
6) wiek
But in console (after running) I get:
1)nazwisko
2)Imie
3)miejsce_urodzenia
4)wiek
5)plec
6)id_dluznik
Why I get other order of column's names?
I've ran into a problem of having to run a number of different queries on the DB (different return types, different number of columns, etc).
While writing that i started to wonder if there's a proper way of writing a helper function.
It seemed that it's really easy to write a function that returns a ResultSet.
However since it a) doesn't close connection b) doesn't close the result set it seems as a possibly working, but improper solution. Is there any place to dump in all results so that they can be returned safely.
(Only thing i could come up with, is just returning a 2D string array (after converting all data to strings) and then converting it all back)
EDIT : Sorry for not writing clear, was wondering if there's any way to just store the result of the query as is (don't need to modify it) without writing a separate method for every possible return type.
The idea behind a 2d string list is being able to store the query values as is.
Col1 Row1 | Col2 Row1 | Col3 Row1
Col1 Row2 | Col2 Row2 | Col3 Row2
EDIT 2 Thank you for replies, i guess i'll just write a small parser for it.
You shouldn't be returning resultSets, you should read the results from the resultset into some kind of container object. A ResultSet is a wrapper around a database cursor, it goes away when the connection closes. It's something you read from and close right away, not something you can pass around your application.
Look at how spring-jdbc does it. You implement a resultSetMapper that is passed to the method on the JdbcTemplate.
Several observations:
You don't need to use Spring to use spring-jdbc. However, I see very little value in reimplementing this stuff yourself.
It's not the job of the code that reads the ResultSet to open and close connections, that needs to be elsewhere.
I'd recommend looking at Spring JDBC. Don't write such a thing yourself. It's already been done, and quite well.
For example, I don't like your idea of returning a List of Strings. You lose a lot of info that way. I'd return a Map of Lists (column view) or List of Maps (row view).
If you must, here are some database utilities that would get you started.
package persistence;
import java.sql.*;
import java.util.*;
/**
* util.DatabaseUtils
* User: Michael
* Date: Aug 17, 2010
* Time: 7:58:02 PM
*/
public class DatabaseUtils {
/*
private static final String DEFAULT_DRIVER = "oracle.jdbc.driver.OracleDriver";
private static final String DEFAULT_URL = "jdbc:oracle:thin:#host:1521:database";
private static final String DEFAULT_USERNAME = "username";
private static final String DEFAULT_PASSWORD = "password";
*/
/*
private static final String DEFAULT_DRIVER = "org.postgresql.Driver";
private static final String DEFAULT_URL = "jdbc:postgresql://localhost:5432/party";
private static final String DEFAULT_USERNAME = "pgsuper";
private static final String DEFAULT_PASSWORD = "pgsuper";
*/
private static final String DEFAULT_DRIVER = "com.mysql.jdbc.Driver";
private static final String DEFAULT_URL = "jdbc:mysql://localhost:3306/party";
private static final String DEFAULT_USERNAME = "party";
private static final String DEFAULT_PASSWORD = "party";
public static void main(String[] args) {
long begTime = System.currentTimeMillis();
String driver = ((args.length > 0) ? args[0] : DEFAULT_DRIVER);
String url = ((args.length > 1) ? args[1] : DEFAULT_URL);
String username = ((args.length > 2) ? args[2] : DEFAULT_USERNAME);
String password = ((args.length > 3) ? args[3] : DEFAULT_PASSWORD);
Connection connection = null;
try {
connection = createConnection(driver, url, username, password);
DatabaseMetaData meta = connection.getMetaData();
System.out.println(meta.getDatabaseProductName());
System.out.println(meta.getDatabaseProductVersion());
String sqlQuery = "SELECT PERSON_ID, FIRST_NAME, LAST_NAME FROM PERSON ORDER BY LAST_NAME";
System.out.println("before insert: " + query(connection, sqlQuery, Collections.EMPTY_LIST));
connection.setAutoCommit(false);
String sqlUpdate = "INSERT INTO PERSON(FIRST_NAME, LAST_NAME) VALUES(?,?)";
List parameters = Arrays.asList("Foo", "Bar");
int numRowsUpdated = update(connection, sqlUpdate, parameters);
connection.commit();
System.out.println("# rows inserted: " + numRowsUpdated);
System.out.println("after insert: " + query(connection, sqlQuery, Collections.EMPTY_LIST));
} catch (Exception e) {
rollback(connection);
e.printStackTrace();
} finally {
close(connection);
long endTime = System.currentTimeMillis();
System.out.println("wall time: " + (endTime - begTime) + " ms");
}
}
public static Connection createConnection(String driver, String url, String username, String password) throws ClassNotFoundException, SQLException {
Class.forName(driver);
if ((username == null) || (password == null) || (username.trim().length() == 0) || (password.trim().length() == 0)) {
return DriverManager.getConnection(url);
} else {
return DriverManager.getConnection(url, username, password);
}
}
public static void close(Connection connection) {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void close(Statement st) {
try {
if (st != null) {
st.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void close(ResultSet rs) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void rollback(Connection connection) {
try {
if (connection != null) {
connection.rollback();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static List<Map<String, Object>> map(ResultSet rs) throws SQLException {
List<Map<String, Object>> results = new ArrayList<Map<String, Object>>();
try {
if (rs != null) {
ResultSetMetaData meta = rs.getMetaData();
int numColumns = meta.getColumnCount();
while (rs.next()) {
Map<String, Object> row = new HashMap<String, Object>();
for (int i = 1; i <= numColumns; ++i) {
String name = meta.getColumnName(i);
Object value = rs.getObject(i);
row.put(name, value);
}
results.add(row);
}
}
} finally {
close(rs);
}
return results;
}
public static List<Map<String, Object>> query(Connection connection, String sql, List<Object> parameters) throws SQLException {
List<Map<String, Object>> results = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = connection.prepareStatement(sql);
int i = 0;
for (Object parameter : parameters) {
ps.setObject(++i, parameter);
}
rs = ps.executeQuery();
results = map(rs);
} finally {
close(rs);
close(ps);
}
return results;
}
public static int update(Connection connection, String sql, List<Object> parameters) throws SQLException {
int numRowsUpdated = 0;
PreparedStatement ps = null;
try {
ps = connection.prepareStatement(sql);
int i = 0;
for (Object parameter : parameters) {
ps.setObject(++i, parameter);
}
numRowsUpdated = ps.executeUpdate();
} finally {
close(ps);
}
return numRowsUpdated;
}
}
You can write helper functions that parse a ResultSet and convert it into an ArrayList or an array or even the fields of an object. For instance, lets say you have a table of orders and then a query returns all of the rows of that table for a particular user (customer). We could then do something like this:
static List<Order> parseOrder(ResultSet rs) {
ArrayList<Order> orderList = new ArrayList<>();
while(rs.next() ) {
Order order = new Order();
order.setID(rs.getInt(1));
order.setCustomerID(rs.getInt(2));
order.setItemName(rs.getString(3));
orderList.add(order);
}
return orderList;
}
Simply turning the result set into an array of an array of Objects would be more general, but probably less useful.
I would leave it up to the calling function to close this ResultSet and possible the PreparedStatement (or Statement) and database connection.
i want to know if we can create a common database class same like we create a connection class and just call getConnection when we need connection to be established.
Basically, i want a database manager class which can handle database operation irrespective of tablename, columncount,etc.
tablename, columnname, values to be inserted would be passed as parameters from servlet.
that way, i can reduce duplication of code. m tryin to make a simple mvc application using jsp-servlets. my database is mysql. i dont know struts, spring, hibernate.
For Example, servlet code will call(databaseManager is the class name.) :
int count=databaseManager.getCount("tableName", "columnName", "value");
and in databaseManager, there will be a function -
public static int getCount(String tableName, String[] arrC, objectArray[] arrV)
{}
similarly, for other functions.
i googled and found out that it could be done using metadata.
but i dont know how to use it.
it would be helpful if u could post code of one function for similar approach.
Check DbUtils component of Apache Commons. Also there are examples provided.
Yes, sure you can. I have done something similar (but not the same) and there can be many approaches. I think you should google more, I'm sure, that there are lot of open source applications for database management/database clients. Try to get inspiration there.
Okay, here is some code for inspiration. It is not totally generic, or what are you looking for, but I think, this could lead you somewhere. If not, throw the stone. :-)
Database provider class:
import java.lang.reflect.Constructor;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.DynaProperty;
public class DatabaseProvider<T extends DatabaseObject> {
private static DatabaseProvider databaseProvider;
private static String connectionString = "";
private static String password = "";
private static String username = "";
private static boolean initialized = true;
public DatabaseProvider(){ }
public static void initDatabaseProvider() {
try {
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
}
catch(SQLException e){
initialized = false;
e.printStackTrace();
}
connectionString = "XXX";
username = "XXX";
password = "XXX";
}
public List<T> performSimpleSelectQuery(String table, String columns, String where, Class targetObj) throws SQLException {
if(!initialized) return null;
List<T> results = new ArrayList<T>();
Constructor ct;
DatabaseObject dbo;
try {
ct = targetObj.getConstructor(null);
dbo = (DatabaseObject)ct.newInstance(null);
}
catch(Exception e){
e.printStackTrace();
return null;
}
String[] cols = columns.split(",");
String[] properties = new String[cols.length];
for(int i = 0; i < cols.length; i++){
cols[i] = cols[i].trim();
properties[i] = dbo.getMappingFromColumnName(cols[i]);
}
Connection conn = DriverManager.getConnection(connectionString, username, password);
PreparedStatement pst = conn.prepareStatement("SELECT " + columns + " FROM " + table + (where.equals("") ? "" : " WHERE " + where));
pst.execute();
ResultSet rs = pst.getResultSet();
while(rs.next()){
try {
dbo = (DatabaseObject)ct.newInstance(null);
for(int i = 0; i < cols.length; i++){
BeanUtils.setProperty(dbo, properties[i], rs.getObject(cols[i]));
}
results.add((T)dbo);
}
catch(Exception e){
e.printStackTrace();
rs.close();
pst.close();
conn.close();
return null;
}
}
rs.close();
pst.close();
conn.close();
return results;
}
public int performInsert(String columns, String values, String table) throws SQLException {
String sqlInsert = "INSERT INTO " + table + " (" + columns + ") VALUES (" + values + ")";
Connection conn = DriverManager.getConnection(connectionString, username, password);
PreparedStatement pst = conn.prepareStatement(sqlInsert);
int toReturn = 0;
try {
toReturn = pst.executeUpdate();
}
catch(Exception e){
e.printStackTrace();
pst.close();
conn.close();
return toReturn;
}
pst.close();
conn.close();
return toReturn;
}
}
Database object class:
import java.util.HashMap;
public abstract class DatabaseObject {
protected HashMap<String, String> dbToBeanMapping = new HashMap<String, String>();
public DatabaseObject() {
initialize();
}
protected abstract void initialize();
public String getMappingFromColumnName(String columnName) {
return dbToBeanMapping.get(columnName);
}
}
Example class:
public class CounterParty extends DatabaseObject {
private String name;
private int instrument;
private int partyId;
public int getPartyId() {
return partyId;
}
public void setPartyId(int partyId) {
this.partyId = partyId;
}
public CounterParty(){}
public int getInstrument() {
return instrument;
}
public void setInstrument(int instrument) {
this.instrument = instrument;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
protected void initialize() {
this.dbToBeanMapping.put("company_name", "name");
this.dbToBeanMapping.put("party_id", "partyId");
this.dbToBeanMapping.put("inst_id", "instrument");
}
}