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
Related
I have two DB and I want to transfer data from DB A tables to DB B tables. All tables from both DB have same syntax. I have this method that takes an argument name of the table. First for loop will travel the columns and I need another for or something else that travels the rows of that specific column. That switch-case retrieve the specific datatype from table column and store it into a field and after I use that field to insert the value in the same place in the DB B. Please some suggestion? I let my trash code below:
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);
// for (int row = 1; row <= nrRows; row++) {
switch (columnType) {
case 4:
int integerValue = rs1.getInt(row);
String integerQuery = "insert into " + nameOfTable + " (" + columnName + ") VALUES(" + integerValue
+ ");";
s2.executeUpdate(integerQuery);
s2.close();
break;
case 12:
String varcharValue = rs1.getString(row);
String varcharQuery = "insert into " + nameOfTable + " (" + columnName + ") VALUES(" + varcharValue
+ ");";
s2.executeUpdate(varcharQuery);
s2.close();
default:
System.out.println("Default");
break;
}
}
// }
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("Transfer done...");
}
}
I use JDBC to get data from sqlite data base. In DB I have 3 column - login, password and role. I try find row by login, but it doesn't work , and I have exeption when I try getString("password") or "role", where is the mistake? Thanks
resSet = statmt.executeQuery("SELECT * FROM users WHERE login='"+login+"';");
if( hasUser( login)){
System.out.println("User finded:");
while(resSet.next()) {
System.out.println("login = " + resSet.getString("login"));
// !exeption
System.out.println("password = " + resSet.getString("password"));
// !exeption
System.out.println("role = " + resSet.getString("role"));
System.out.println();
}
}else{
System.out.println( "User not found");
}
You can check to see what column names are being returned.
ResultSetMetaData rsmd = resSet.getMetaData();
int colCount = rsmd.getColumnCount();
String rValue = "";
for (int i = 1; i <= colCount ; i++){
String name = rsmd.getColumnName(i);
rValue += name + " ";
}
System.out.println(rValue);
ResultSetMetaData metaData = resultSet.getMetaData();
int count = metaData.getColumnCount(); //number of column
String columnName[] = new String[count];
for (int i = 1; i <= count; i++)
{
metaData.getColumnLabel(i));
}
Refer to this ans..ans see what are the names of your columns in resultant set
http://stackoverflow.com/questions/19094999/java-how-to-get-column-name-on-result-set
Hello and thank you for reading my post.
I have a PostgreSQL table "t" with a column "c" which type is "character varying(32)".
Values in this column look like this: "2014100605".
I am using the "MAX()" aggregate function to retrieve the maximum value in this column.
SELECT MAX(c) AS max FROM t;
In Java, if I prepare the query above, get a "resultSet" object and send it the getString("max") message, I get max = null.
If I send it the getInt("max") method instead, I get the result I'm expecting, something like "2014100605".
Is this normal behavior?
Am I really allowed to do this or is it by chance I'm getting the expected result?
Is "MAX()" actually using the lexicographical order?
Best regards.
A bit of Java code:
s_preparedSqlQuery =
"SELECT MAX(quotinv_nro) AS quotinv_nro_max "
+ "FROM imw_quotation_invoice "
+ "WHERE quotinv_type = ? "
+ "AND quotinv_nro LIKE '" + s_quotinvDate + "%'";
preparedStatement = m_connection.prepareStatement(s_preparedSqlQuery);
preparedStatement.setString(1, s_quotinvType);
resultSet = preparedStatement.executeQuery();
if(resultSet != null)
{
if(resultSet.next())
{
// s_quotinvNroMax = resultSet.getString("quotinv_nro_max");
n_quotinvNroMax = resultSet.getInt("quotinv_nro_max");
// if(s_quotinvNroMax == null)
if(n_quotinvNroMax == 0)
{
n_nbQuotinvsThisSameDate = 0;
return n_nbQuotinvsThisSameDate;
}
else
{
s_quotinvNroMax = Integer.toString(n_quotinvNroMax);
n_length = s_quotinvDate.length();
s_currentMaxNro = s_quotinvNroMax.substring(n_length - 1);
n_nbQuotinvsThisSameDate = Integer.valueOf(s_currentMaxNro);
}
}
}
If you are hitting on a unique Id column....
int maxID = 0;
Statement s2 = con.createStatement();
s2.execute("SELECT MAX(UniqueId) FROM MyTable");
ResultSet rs2 = s2.getResultSet();
if (rs2.next())
{
maxID = rs2.getInt(1);
}
If you are hitting on any other non-key column....
int maxID = 0;
Statement s2 = con.createStatement();
s2.execute("SELECT MAX(ColumnValue) FROM MyTable");
ResultSet rs2 = s2.getResultSet();
while (rs2.next())
{
maxID = rs2.getInt(1);
}
I'm using MySQL commands via JDBC (Java) to make changes to my database. I have implemented the following method to return the values of a column. The goal is to have the location in the column (row) correspond with their location in the array (index). This works with String columns, but with numerical columns, the ResultSet seems to place them in ascending order, thus making their positioning in the returned String array not reflect their positioning in the column. 'rs' is a ResultSet reference variable.
public String[] getColumnContents(String tableName, String columnName) {
String sql = "SELECT " + columnName + " FROM " + tableName;
String[] results = new String[SQLManager.getColumnLength(tableName, columnName)];
try {
rs = statement.executeQuery(sql);
for (int counter = 0; rs.next(); counter++) {
results[counter] = rs.getString(columnName);
}
} catch (SQLException e) {
e.printStackTrace();
}
return results;
}
It's as simple as adding an ORDER BY clause to the SQL command. Here's my working method:
public String[] getColumnContents(String tableName, String columnName) {
String sql = "SELECT " + columnName + " FROM " + tableName + " ORDER BY " + columnName1 + " ASC, " + columnName2 + " ASC";
String[] results = new String[SQLManager.getColumnLength(tableName, columnName)];
try {
rs = statement.executeQuery(sql);
for (int counter = 0; rs.next(); counter++) {
results[counter] = rs.getString(columnName);
}
} catch (SQLException e) {
e.printStackTrace();
}
return results;
}
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