The code below displays the tuples of an specific table. How can I turn this into dynamic code? So the user would enter the name of the table, then the rows and column names in addition to the content of the table are displayed.
* Keep in mind that res.getInt and res.getString need to be specified as they are. In a dynamic model, I wouldn't need to know the number, type, and name of the columns. *
public void displayTableA()
{
//Connection already established
Statement st = conn.createStatement();
ResultSet res = st.executeQuery("SELECT * FROM A");
System.out.println("A_code: " + "\t" + "A_name: ");
while (res.next()) {
int r = res.getInt("A_code");
String s = res.getString("A_name");
System.out.println(r + "\t\t" + s);
}
conn.close();
}
Direct answer: The query is just a string. You can build it up from user inputs. Like read the name of the table into a variable, say "String tablename", then
String query="select * from " + tablename;
Then you run the query to get the result set:
ResultSet rs=st.executeQuery(query);
Then get the meta data for the result set:
ResultSetMetaData meta=rs.getMetaData();
Then loop through the columns getting their names:
for (int x=1;x<=meta.getColumnCount();++x)
{
String columnName=meta.getColumnName(x);
... do whatever you want with this column name ...
}
(Note the columns are numbered starting from 1, not 0.)
As to the data itself, if you're just dumping it out, you don't need to know the type. Just do getString on everything. Every data type can be converted to a string. Well, if you have blobs or images you might want to check for those. There's a ResultSetMetaData function to get the column type, I think it's getType or something like that. Check the javadocs.
That said, why do you want to do this? If you're building some sort of tool to be used by developers to do ad hoc queries, okay fine. But I would be extremely cautious about exposing something like this to end users. (a) They would be unlikely to understand the data, and (b) You'd be creating a huge security hole, users could see ANY data in the system. You could potentially wrap this in checks to limit the users to what they're authorized to see, but it's a lot of work to get that right. It's a lot easier to say "here's what you are allowed to see" then to try to say "here's what you're not allowed to see".
public void displayTable(String table)
{
//Connection already established
Statement st = conn.createStatement();
ResultSet res = st.executeQuery("SELECT * FROM " + table);
ResultSetMetaData rsmd = res.getMetaData();
while (res.next()) {
for(int ii = 1; ii <= rsmd.getColumnCount(); ii++) {
// get type
int type = rsmt.getColumnType(ii);
String value = null;
switch (type) {
case Types.VARCHAR: value = res.getString(ii); break;
}
// print value.
System.out.print(rsmd.getColumnName(ii) + ": " + value);
}
}
conn.close();
}
If you want print a ResultSet in a dynamic query converting a instance of ResultSet in String:
public static String toString(ResultSet rs) throws SQLException {
StringBuilder sb = new StringBuilder();
ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
Map<Integer, Integer> sizeMap = new HashMap<>();
for (int column = 1; column <= columnCount; column++) {
int size = Math.max(metaData.getColumnDisplaySize(column), metaData.getColumnName(column).length());
sizeMap.put(column, size);
sb.append(StringUtils.rightPad(metaData.getColumnName(column), size));
sb.append(' ');
sb.append(' ');
}
sb.append('\n');
for (int column = 1; column <= columnCount; column++) {
sb.append(StringUtils.rightPad("", sizeMap.get(column), '-'));
sb.append(' ');
sb.append(' ');
}
while(rs.next()) {
sb.append('\n');
for (int columnIndex = 1; columnIndex <= columnCount; columnIndex++) {
String str = rs.getString(columnIndex);
if (str == null) {
str = "(null)";
}
sb.append(StringUtils.rightPad(str, sizeMap.get(columnIndex)));
sb.append(' ');
sb.append(' ');
}
}
return sb.toString();
}
For print something like that:
user_id user_code date_update
------------ -------------------- -------------------
01006393 00989573 2011-09-29 19:23:46
00984742 20192498 2011-12-21 00:00:00
This method use Commons Lang 3
Note: You should use this with care for avoid memory errors. You can change the sb.append with System.out.print or System.out.println
Related
I have 2 DB with multiple tables. All tables have the same syntax. Here I have a method that takes name of the table as an argument. The table that I try to insert is with 3 columns (int, varchar, int). The problem is, only the first row is inserted, and the 2 and 3 row is NULL, I don't know what is the problem. Any suggestions, please?
public void getAndInsertData(String nameOfTable) {
try {
Class.forName("com.mysql.jdbc.Driver");
Connection con1 = DriverManager.getConnection(urlDB1, user1, password1);
Statement s1 = con1.createStatement();
Connection con2 = DriverManager.getConnection(urlDB2, user2, password2);
Statement s2 = con2.createStatement();
ResultSet rs1 = s1.executeQuery("SELECT * FROM " + nameOfTable);
ResultSetMetaData rsmd1 = rs1.getMetaData();
int columnCount = rsmd1.getColumnCount();
for (int column = 1; column <= columnCount; column++) {
String columnName = rsmd1.getColumnName(column);
int columnType = rsmd1.getColumnType(column);
while (rs1.next()) {
switch (columnType) {
case Types.INTEGER:
case Types.SMALLINT:
case Types.BIGINT:
case Types.TINYINT:
int integerValue = rs1.getInt(column);
String integerQuery = "insert into " + nameOfTable + " (" + columnName + ") VALUES("
+ integerValue + ");";
s2.executeUpdate(integerQuery);
break;
case Types.VARCHAR:
case Types.NVARCHAR:
case Types.LONGNVARCHAR:
case Types.LONGVARCHAR:
String varcharValue = rs1.getString(column);
String varcharQuery = "insert into " + nameOfTable + " (" + columnName + ") VALUES("
+ varcharValue + ");";
s2.executeUpdate(varcharQuery);
default:
System.out.println("Default");
break;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
Your integerQuery and varcharQuery both insert into datebase table a record with one filled column and blank other columns. Because you provide value to one column only.
A few issues:
Use try-with-resources to make sure the JDBC resources are cleaned up correctly.
No need for a switch statement, because we don't actually need to know the types of the columns. The JDBC driver will handle that if you use getObject() and setObject().
Only execute one INSERT per row from the source table.
Use batching when inserting a lot of records, for better performance.
Here is how to do it:
try (
Connection conSource = DriverManager.getConnection(urlDB1, user1, password1);
Connection conTarget = DriverManager.getConnection(urlDB2, user2, password2);
Statement stmtSource = conSource.createStatement();
ResultSet rsSource = stmtSource.executeQuery("SELECT * FROM " + nameOfTable);
) {
// Build insert statement
ResultSetMetaData metaData = rsSource.getMetaData();
int columnCount = metaData.getColumnCount();
StringBuilder sql = new StringBuilder("INSERT INTO " + nameOfTable + " (");
for (int column = 1; column <= columnCount; column++) {
if (column != 1)
sql.append(", ");
sql.append(metaData.getColumnName(column));
}
sql.append(") VALUES (");
for (int column = 1; column <= columnCount; column++) {
if (column != 1)
sql.append(", ");
sql.append("?");
}
sql.append(")");
// Copy data
conTarget.setAutoCommit(false);
try (PreparedStatement stmtTarget = conTarget.prepareStatement(sql.toString())) {
int batchSize = 0;
while (rsSource.next()) {
for (int column = 1; column <= columnCount; column++) {
// Copy row here. Use switch statement to control the mapping
// if source and target table columns don't have compatible types.
// The following statement should work for most types, so switch
// statement only needs to cover the exceptions.
stmtTarget.setObject(column, rsSource.getObject(column), metaData.getColumnType(column));
}
stmtTarget.addBatch();
if (++batchSize == 1000) { // Flush every 1000 rows to prevent memory overflow
stmtTarget.executeBatch();
batchSize = 0;
}
}
if (batchSize != 0)
stmtTarget.executeBatch();
}
conTarget.commit();
}
As The Impaler already mentioned, your loops are at the wrong place.
For every record of rs1, you want to insert one record using s2.
You can build a prepared statement first using the metadata and then inject the values:
ResultSetMetaData rsmd1 = rs1.getMetaData();
int columnCount = rsmd1.getColumnCount();
StringBuffer sql=new StringBuffer("insert into "+nameOfTable+" (");
for (int column = 1; column <= columnCount; column++) {
String columnName = rsmd1.getColumnName(column);
if(column>1)
sql.append(",");
sql.append(columnName);
}
sql.append(") values (");
for(int i=1;i<=columnCount;i++)
{
sql.append((i==1?"":",")+ "?");
}
sql.append(")");
System.out.println("Prepared SQL:"+sql.toString());
// sql = insert into nameOfTable (col1,col2,col3) values (?,?,?)
PreparedStatement s2= con2.prepareStatement(sql.toString());
while (rs1.next()) {
s2.clearParameters();
for (int column = 1; column <= columnCount; column++) {
int columnType = rsmd1.getColumnType(column);
switch (columnType) {
case Types.INTEGER:
case Types.SMALLINT:
case Types.BIGINT:
case Types.TINYINT:
s2.setInt(column, rs1.getInt(column));
break;
case Types.VARCHAR:
case Types.NVARCHAR:
case Types.LONGNVARCHAR:
case Types.LONGVARCHAR:
s2.setString(column, rs1.getString(column));
break;
default:
System.err.println("Not supported type for column "+column+" with type:"+columnType);
s2.setNull(column, columnType);
break;
}
} // end of for loop
// execute statement once per record in rs1
s2.executeUpdate();
} // end of while
I have an hashmap with a key and value (Array of integers) HashMap<key, array> and I would like to retrieve data from a sql table with a column that matches the values in array stored in a hashmap
I can do this in two ways one getting all the data from table and iterating through hashmap and other being iterating through hashmap and applying the where clause in each iteration which makes n calls to database.
Is there an efficient way to do this other than the above ?
Use a PreparedStatement to set up a query having an IN condition in the WHERE clause. This snippet should give you the general idea:
values = new int[]{1,2,3,4,5};
String query =
"SELECT film_id, title\n"
+ "FROM film\n"
+ "WHERE film_Id IN (";
for (int i = 0; i < values.length; ++i) {
if (i > 0) {
query += ",";
}
query += "?";
}
query += ")";
try {
Connection conn = DriverManager.getConnection("db-url", "user", "pwd");
PreparedStatement stmt = conn.prepareStatement(query);
for (int i = 0; i < values.length; ++i)
stmt.setInt(i+1, values[i]);
ResultSet rs = stmt.executeQuery();
while (rs.next()){
// process results
}
} catch (Exception ex) {
ex.printStackTrace();
}
I really need help. I am trying to retrieve a data from in my database using ResultSetMetaData and ResultSet. Also I am using a stored Procedure. The program is run find, however when I run it and be expecting 3 result from the row, instead it will give me 2. For example if I ask a user to enter a position and there are 6 player in that position, it will give me 5 players instead of 6 player.
Here is my code
if (!rs.next()) {
System.out.println("There is no match in the database " + position);
}
else {
System.out.println(String.format(" " + " %-19s %s", "Name",
"Rank") + "\t" + String.format("%-11s %s", "Position",
"School") + "\t" + String.format("%-6s %s",
"Age", "War") + "\n-----------------"
+ "-------------------------------------------------");
while (rs.next()) {
int rank = 0, age = 0;
String name = null, pos = null, sch = null;
double war = 0;
for (int i = 1; i < colum - 1; i++) {
rank = rs.getInt(1);
name = rs.getString(2);
pos = rs.getString(3);
sch = rs.getString(4);
age = rs.getInt(5);
war = rs.getDouble(6);
}
When I run my java code I get this result. It's not getting the first index in the list
When I call my stored procedure in MYSQL Workbench, I get this result
You have read first index in
if (!rs.next()) {
itself. This will move the cursor to the next row. You will have to remove this and it will give all the rows.
This code prints 4 data headers and data below in my CSV file correctly in columns A-D
However, in columns E-H there is a Random set of data.
ResultSetMetaData rsmd = result.getMetaData();
int columnCount = rsmd.getColumnCount();
for (int i = 1; i < columnCount + 1; i++) {
String name = rsmd.getColumnName(i);
out.print(name);
if (i != columnCount + 1) {
out.print(",");
System.out.print(i);
}
}
while (result.next()) {
out.println(String.format("%s,%s,%s,%s", result.getString(1),
result.getString(2), result.getString(3),
result.getString(4)));
}
result.close();
statement.close();
There is no newline character being written after the column names have been written, resulting in the first row of data appearing as column headers. Write a newline character after the for loop that writes the column names.
This question already has answers here:
Retrieve column names from java.sql.ResultSet
(14 answers)
Closed 8 years ago.
Hello I'm trying to make an error when there is no matched student...
and it will display like this
No matching records found and I want the column name still the same but still not figuring it out... can some one tell me if this is right??
Heres my function for that... and I add comment there where I put the error... but i don't know how to get the columnname
public void SearchTableStudent() {
String tempSearchValue = searchStudent.getText().trim();
boolean empty = true;
sql = "SELECT student_id as 'Student ID',"
+ "concat(lastname, ' , ', firstname, ' ', middlename) as 'Name'"
+ "FROM user "
+ "WHERE CAST(student_id as CHAR) LIKE '%" + tempSearchValue + "%'";
try {
pst = conn.prepareStatement(sql);
rs = pst.executeQuery();
while(rs.next()) {
table.setModel(DbUtils.resultSetToTableModel(rs));
empty = false;
}
if(empty) {
String error = "";
table.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
{"No matching records found",null}
},
new String [] {
/** I WANT TO PUT THE SAME COLUMN NAME ON MY DATABASE SELECTED BUT DON't Know
WHAT FUNCTION TO DO*/
}
));
}
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e.getMessage());
}
}
I try like this but still gave me NULL!!!
this code is below of empty = false;
for(int i=0; i<table.getColumnCount(); i++) {
test[i] = table.getColumnName(i);
}
ResultSetMetaData metaData = resultSet.getMetaData();
int count = metaData.getColumnCount(); //number of column
String columnName[] = new String[count];
for (int i = 1; i <= count; i++)
{
columnName[i-1] = metaData.getColumnLabel(i);
System.out.println(columnName[i-1]);
}
Try this.
ResultSetMetaData meta = resultset.getMetaData();
Integer columncount = meta.getColumnCount();
int count = 1 ; // start counting from 1 always
String[] columnNames = new String[columncount];
while(count<=columncount){
columnNames [count-1] = meta.getColumnLabel(count);
count++;
}
Since here your expecting is to get the columns alias instead of column name, so you have to use ResultSetMetaData.getColumnLabel instead of ResultSetmetaData.getColumnName.
Get ResultSetMetaData using ResultSet#getMetaData():
ResultSetMetaData meta = rs.getMetaData();
And then to get column name of 1st column:
String col1Name = meta.getColumnLabel(1);
Similarly to get column name of 2nd column:
String col2Name = meta.getColumnLabel(2);
Get the metadata
ResultSetMetaData metaData = rs.getMetaData();
Then you can do:
String columnName = metaData.getColumnName(int index);
ResultSetMetaData doc
rs.getMetaData().getColumnName(int i);
and do not concat the query param!