How can I query data from my database and assign it to an array of strings? In this attempt I noticed I would receive an out of bounds error before I included the resultSet.next() call since it seems that ResultSet starts at 0 and is not called like a list / array (meaning you can access the contents with its index).
public String[][] retrieveNameAndLocation() {
final String table = "customers";
try {
ResultSet resultSet = statement.executeQuery(
"SELECT " +
"first_name," +
"location" +
" FROM " + table
);
resultSet.next();
final String[] names = (String[]) (resultSet.getArray(1).getArray());
final String[] location = (String[]) (resultSet.getArray(2)).getArray();
final String[][] nameAndCountry = {names, location};
resultSet.close();
return nameAndCountry;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
Anyways the above code resulted in a SQLFeatureNotSupportedException. My next attempt was to simply call the the columns by name since I noticed it was an option inside of getArray, however that also resulted in the not supported exception.
public String[][] retrieveNameAndLocation() {
final String table = "customers";
try {
ResultSet resultSet = statement.executeQuery(
"SELECT " +
"first_name," +
"location" +
" FROM " + table
);
resultSet.next();
final String[] names = (String[]) (resultSet.getArray("first_name").getArray());
final String[] location = (String[]) (resultSet.getArray("location")).getArray();
final String[][] nameAndCountry = {names, location};
resultSet.close();
return nameAndCountry;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
I am not really sure why I need to include resultSet.next() because it seems like it's just broken since why would they include an option to query columns if they forced you to loop through the indexes?
I think you misunderstand the purpose of method getArray. Some DBMSs, like Oracle, have "array" data types. Hence the getArray method – to query a database table column whose type is an array type. I have no experience with MySQL but it appears that it does not have an array type. Hence the JDBC driver for MySQL does not need to implement the getArray method and that's why you get the SQLFeatureNotSupportedException.
You need to iterate through the ResultSet and build up your array. However since you usually don't know how many rows there are in a ResultSet, I usually use a List and then, if required, convert it to an array because when you declare an array you need to know its size.
I would also define a record and declare a List of records.
(Note that below code is not compiled and not tested since I don't have your database and I can't simulate it since the code in your question is not a minimal, reproducible example.)
public record NameAndCountry(String name, String location) {
public static java.util.List<NameAndCountry> retrieveNameAndLocation() {
final String table = "customers";
try {
ResultSet resultSet = statement.executeQuery(
"SELECT " +
"first_name," +
"location" +
" FROM " + table
);
java.util.List<NameAndCountry> list = new java.util.ArrayList<>();
while (resultSet.next()) {
String name = resultSet.getString(1);
String location = resultSet.getString(2);
NameAndCountry row = new NameAndCountry(name, location);
list.add(row);
}
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
}
Related
I wrote a small program to retrieve records in my email queue table to process and send email. While using JDBC to achieve this, as shown below:
MySqlConnect con=new MySqlConnect();
public PreparedStatement preparedStatement = null;
public Connection con1 = con.connect();
//pick up queue and send email
public void email() throws Exception {
try{
while(true) {
String sql = "SELECT id,user,subject,recipient,content FROM emailqueue WHERE status='Pending' ";
PreparedStatement statement = con1.prepareStatement(sql);
ResultSet rs = statement.executeQuery();
while (rs.next()) {
String subject = rs.getString("subject");
String recipient = rs.getString("recipient");
String content = rs.getString("content");
String id = rs.getString("id");
String username = rs.getString("user");
String emailStatus = "DONE";
String errormsg=sendEmail(recipient, subject, content, id,username);
if (!errormsg.equals("")) {
emailStatus = "FAILED";
}
TerminalLogger.printMsg("Status : " + emailStatus);
}
statement.close();
rs.close();
}
}
}catch(Exception e){
e.printStackTrace();
TerminalLogger.printMsg("Exception: "+e.toString());
}
con1.close();
Thread.sleep(2000);
}
The above works fine since it retrieve email records where the status is pending and pass the subject, content, recipient, etc to sendemail method and done.
I wanted to achieve the same goal but using JPA persistence instead. So I wrote this method:
public Object getrecords() {
try {
String sql = "select p.id,p.user,p.subject,p.recipient,p.content from Emailqueue p where " +
"status='Pending'";
List<Object[]> resList =(List<Object[]>) em.createQuery(sql).getResultList();
if (resList == null) {
throw new Exception("Error with selection query.");
}
if (resList.size() > 0) {
return resList;
}
// msg = "Setting <" + name + "> not found.";
return null;
} catch (Exception e) {
msg = CoreUtil.wrapMsg(CoreUtil.FUNC_ERROR,
this.getClass().getName(), "get(" + "Pending" + ")", e.getMessage());
return null;
}
}
It has no issue retrieving the records and size in the table. And I called this method in the email(). Something like:
Object records = ejbCon.getSettingsFacade().getrecords();
From here, I can't figure how do I loop the records, to get each of the value in each field. For example, if there's 2 pending email records, I should need to retrieve each of its content, subject, recipient, etc and pass to sendemail method.
Also, retrieving these records is already taxing to my program since it takes long time to close the application compared to using JDBC (it's faster and I guess more efficient?).
So, I also need to consider the performance by doing using this method.
Edit
I should have clarified this better to explain what I'm trying to achieve.
So I have that email queue table in this form:
id user subject content recipient status
1 user1 test example abc#example.com Pending
2 user2 test2 example cde#example.com Pending
So before this, I was using resultset in JDBC to obtain each individual field values for id:1 and pass them to the send method, and proceed with id:2 and do the same, an iteration. Now I want to achieve the same way by using the object I retrieve but I can't specify which fields value it's able to get. So I am stuck.
There are many ways to achieve your desired result but the easiest way you can do is to create a parameterized constructor in your Emailqueue class and change your query to
String sql = "select NEW
Emailqueue(p.id,p.user,p.subject,p.recipient,p.content) from Emailqueue p
where " +
"p.status='Pending'";
List<Emailqueue> resList =(List<Emailqueue>)
em.createQuery(sql).getResultList();
this way you can normally iterate over List using foreach loop.
If i understood what is your correctly needs, Try to loop your resList.
And i recommend to you don't get Object type.
List<Emailqueue> resList = em.createQuery(sql, Emailqueue.class).getResultList();
for (Emailqueue email : resList) {
String subject = email.getSubject();
// something do.
}
Here the simple way to get data based on query.
If you are using Entity Manager for query and use HQL, u can just simply return the class data. here is the sample :
TypedQuery q = man.createQuery("SELECT u FROM YourTable u WHERE u.col1 =:col1 and u.col2 =:col2", YourCalss.class)
.setParameter("col1", col1)
.setParameter("col2", col2);
result = q.getResultList();
If you want with custom result, then u can modify the current class of entity into another class.
List<CustomClass> result = null;
EntityManager man = PersistenceUtilities.getEntityManagerFactory().createEntityManager();
try {
TypedQuery q = man.createQuery("SELECT NEW " + CustomClass.class.getCanonicalName() + "(u.col1,u.col2) "
+ "FROM YourTable as u ", CustomClass.class);
if (q.getResultList().size() > 0) {
result = q.getResultList();
}
} catch (Throwable t) {
Main.logger.error(t.getMessage());
} finally {
man.close();
}
return result;
I want to add values in a table that has dynamic columns.
I managed to create a table with dynamic columns but I cannot figure out how to insert data.
//Create Table
sql = "CREATE TABLE MyDB.myTable" +
"(level INTEGER(255) )";
int columnNumber = 5; //Number of columns
//Add columns
for (i=0;i<columnNumber;i++){
String columnName = "Level_" +i:
String sql = "ALTER TABLE MyDB.myTable ADD " + columnName + " INTEGER(30)";
}
//Insert Data
//How to insert data dynamically, without knowing the number of columns?
You can also use database metadata to get the column names. This has the advantage that you even don't need to know the column names, rather they are retrieved dynamically in your code.
public static List<String> getColumns(String tableName, String schemaName) throws SQLException{
ResultSet rs=null;
ResultSetMetaData rsmd=null;
PreparedStatement stmt=null;
List<String> columnNames =null;
String qualifiedName = (schemaName!=null&&!schemaName.isEmpty())?(schemaName+"."+tableName):tableName;
try{
stmt=conn.prepareStatement("select * from "+qualifiedName+" where 0=1");
rs=stmt.executeQuery();//you'll get an empty ResultSet but you'll still get the metadata
rsmd=rs.getMetaData();
columnNames = new ArrayList<String>();
for(int i=1;i<=rsmd.getColumnCount();i++)
columnNames.add(rsmd.getColumnLabel(i));
}catch(SQLException e){
throw e;//or log it
}
finally{
if(rs!=null)
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
throw e
}
if(stmt!=null)
try {
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
throw e
}
}
return columnNames;
}
Once you have the column names, you can use it as you normally would (List.size() would of course give the number of columns).
UPDATE:
//I will assume that your values (data to be inserted) is a List of Object types and that it is already populated
List<Object> data = new ArrayList<>();
//you will populate this list
//getting the column names
List<String> columnNames = getColumns("MyTable", "MyDB");
String insertColumns = "";
String insertValues = "";
if(columnNames != null && columnNames.size() > 0){
insertColumns += columnNames.get(0);
insertValues += "?";
}
for(int i = 1; i < columnNames.size();i++){
insertColumns += ", " + columnNames.get(i) ;
insertValues += "?";
}
String insertSql = "INSERT INTO MyDB.MyTable (" + insertColumns + ") values(" + insertValues + ")";
try{
PrepareStatement ps = conn.prepareStatement(insertSql);
for(Object o : data){
ps.setObject(o); //you must pass objects of correct type
}
ps.execute(); //this inserts your data
}catch(SQLException sqle){
//do something with it
}
This code assume that you pass objects of correct types to PreparedStatement.setObject(Object o) method. It's also possible to retrieve column types using metadatabase information and then use that info to enforce type checking but that would make your code much more complicated
If you know the number of columns you want to insert, you can make your insert query same way you made your CREATE TABLE. Explicitly name the columns you want to add your data into, and make sure the columns you leave empty are allowed to be empty (NULL or default=0)
INSERT INTO MyDB.myTable (Level_1, Level_2, ...) VALUES (Val_1, Val_2, ...);
The alternative approach would be to store each inserted value in a separate row. In that way you don't vhave to change the number of columns in your table.
You need a table where you have a ID for every group of values:
- ID
- Level
- Value
You could have a separate table where you can register each ID:
- ID (bigInt, auto increment, primary key)
- info field
- timestamp
Now, for every set of data you want to insert, first insert need a Unique ID. If you use the second table, inserting a new row would give you a new ID:
INSERT INTO register_table (ID, info, timestamp) VALUES (NULL, 'some info', NOW());
This will give you a new ID (last_inserted_id).
With this ID now insert all values in the other table:
for(i=0i<columnNumber;i++){
"INSERT INTO _table (ID, Level, _Value) VALUES ("+ the_ID +", "+ i +", "+ the_VALUE +");";
}
If you want to fetch the data:
"SELECT Level, _Value FROM _table WHERE ID="+ the_ID +" ORDER BY Level;";
i have been trying a lot of syntax but i can't figured it out.
I want to display "Invoice number" from my databases to the text field (The field "invoice" from table "transaksi" is already filled with number) But it won't show anything. Here's my code :
private void InvoiceActionPerformed(java.awt.event.ActionEvent evt) {
ResultSet aa = null;
try {
koneksi objKoneksi = new koneksi();
Connection kon = objKoneksi.bukaKoneksi();
Statement stat = kon.createStatement();
String query ="select invoice from transaksii";
String res = aa.getString(query);
Invoice.setText(res);
}
catch (SQLException e) {}
}
Note : - Invoice is Former JTextField1.
- transaksii = name of my table.
- invoice = name of the field.
Thank you. English is not my native language.
You did not execute the query. The value of aa is null and you might be getting a NullPointerException.
String query ="select invoice from transaksii";
aa=stat.executeQuery(query);
if(aa.next())
String res = aa.getString("invoice");
Note: Its not a good practice to have an empty catch because you would then end up without knowing about the exception like now. You should do e.printStackTrace(); in catch block.
There is only 1 problem in your original code. The result set that has returned in the line aa = stat.executeQuery(query); is presently at the a position before the 1st result returned. So When you call aa.getString it is not going to return any thing as it is before the 1st result. Also the ResultSet.getString takes a parameter of int or a parameter of String representing the column name. But in your case you have passed the query as the parameter.
So the corrected code should be if you only want to return the 1st item of the ResultSet would be :
private void InvoiceActionPerformed(java.awt.event.ActionEvent evt) {
ResultSet aa = null;
try {
koneksi objKoneksi = new koneksi();
Connection kon = objKoneksi.bukaKoneksi();
Statement stat = kon.createStatement();
String query ="select invoice from transaksii";
aa = stat.executeQuery(query);
String res = "";
if(aa.next()){ // checks and moves it to 1st position
res = aa.getString("invoice"); //column name of the column
}
Invoice.setText(res);
}catch(SQLException e){
e.printStackTrace();
}finally{
//The closing of connection, statement and resultset.
}
}
I'm trying to store rows from a table into an array. I can get the first result and store that but I cannot seem to be able to store any of the other data.
This is the code I've written
try
{
test = "select * from Arsenal order by 'MatchNumber' ASC";
rs = st.executeQuery(test);
while (rs.next())
{
//This retrieves each row of Arsenal table and adds it to an array in the Team Results class.
matchno = rs.getString("MatchNumber");
hometeam = rs.getString("HomeTeam");
awayteam = rs.getString("AwayTeam");
homegoals = rs.getString("HomeGoals");
awaygoals = rs.getString("AwayGoals");
result = rs.getString("Result");
teams = (matchno + "," + hometeam + "," + awayteam + "," + homegoals + "," + awaygoals + "," + result); // Takes all the variables containging a single customers information and puts it into a string, seperated by commas.
TeamResults.add(matchno,hometeam,awayteam,homegoals,awaygoals,result);
}
}
Any idea where I'm going wrong?
Change the while-condition to hasNext() and use next() inside of the loop to move the database cursor forward.
Try to use this method bellow :
public void SelectData(String sqlcounter ,String sql){
try {
RsCounter=stmt.executeQuery(sqlcounter);
System.out.println(sqlcounter);
while(RsCounter.next()){
countrow=RsCounter.getInt("COUNTR");
System.out.println(countrow+"\n");
}
System.out.println(sql);
RsSelecting = stmt.executeQuery(sql);
data=new String[countrow][RsSelecting.getMetaData().getColumnCount()];
header= new String[RsSelecting.getMetaData().getColumnCount()];
i=0;
while(RsSelecting.next()){
for(j=0;j<RsSelecting.getMetaData().getColumnCount();j++){
data[i][j]=(RsSelecting.getString(j+1));
header[j]=RsSelecting.getMetaData().getColumnName(j+1);
System.out.print(data[i][j]+"\n");
}
i++;
}
i=j=0;
} catch (SQLException ex) {
ex.printStackTrace();
Logger.getLogger(Connect.class.getName()).log(Level.SEVERE, null, ex);
}
}
where
sqlcounter ="select COUNT(*) as COUNTR from Arsenal order by 'MatchNumber' ASC";
and
sql ="select * from Arsenal order by 'MatchNumber' ASC";
Verify the column names once. Sometimes ALIAS doesn't work out, I am not sure why.
Get the meta data from the result set:
ResultSetMetaData metaData = resultSet.getMetaData();
int size = metaData.getColumnCount();
for (int i = 0; i < size; i ++)
System.out.println(metaData.getColumnName(i);
Also just for performance, list out the column names instead of using * in the SELECT query. Also, you can take a look at com.sun.rowset.CachedRowSetImpl. It's used like:
CachedRowSetImpl crs = new CachedRowSetImpl();
crs.populate(resultSet);
I think it also implements CachedRowSet, but I am not entirely sure.
I'm working on a project where a user can assemble the components of a yoga class. It's spread across several files and thus too large to put it all here. The trouble I'm having is in one method where I need to iterate over the horizontal columns of a result set that is returning only ONE ROW from a MySQL database.
I understand that I have to position the cursor on the first row of the result set (which I believe I am doing). Since I have only one row in the result set (my variable is rset), I should be using rset.next() only one time, correct? And then I should be able to use a simple loop to iterate over each column and append the value to my String Builder. I want to skip the first column and append each subsequent value until the loop reaches columns with null values. I cannot find why my code keeps returning a "Before start of result set" exception.
Can anyone spot anything wrong?
I'll post the method as well as the method called by this method. (I posted this in a different question, but I believe the nature of my question has changed, so I'm re-posting this with a different title.)
// Query that returns the poses within a specific section
public String listPosesInSection(String tableName, String sectionName) {
String strList;
StringBuilder strBuilderList = new StringBuilder("");
// Run the query
try {
statement = connection.createStatement();
// Query will collect all columns from one specific row
rset = statement.executeQuery("SELECT * FROM " + tableName + " WHERE " + tableName + "_name = '" + sectionName + "'");
rset.next();
System.out.println("Column count is " + countColumnsInTable(tableName));
for (int i = 2; i <= countColumnsInTable(tableName); i++) {// First value (0) is always null, skip first column (1)
System.out.println("test");
strBuilderList.append(rset.getString(i) + "\n"); // This is line 126 as indicated in the exception message
}
} catch (SQLException e) {
e.printStackTrace();
}
strList = strBuilderList.toString();
return strList.replaceAll(", $",""); // Strips off the trailing comma
}
// Method for getting the number of columns in a table using metadata
public int countColumnsInTable(String sectionType) {
int count = 16;
try {
statement = connection.createStatement();
rset = statement.executeQuery("SELECT * FROM " + sectionType);
rsMetaData = rset.getMetaData();
count = rsMetaData.getColumnCount();
} catch (SQLException e) {
e.printStackTrace();
}
return count;
}
And here is the first part of the exception message:
Column count is 26
java.sql.SQLException: Before start of result set
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1078)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:989)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:975)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:920)
at com.mysql.jdbc.ResultSetImpl.checkRowPos(ResultSetImpl.java:855)
at com.mysql.jdbc.ResultSetImpl.getStringInternal(ResultSetImpl.java:5773)
at com.mysql.jdbc.ResultSetImpl.getString(ResultSetImpl.java:5693)
at YogaDatabaseAccess.listPosesInSection(YogaDatabaseAccess.java:126)
at YogaSectionDesigner$5.actionPerformed(YogaSectionDesigner.java:231)
Looks to me like you're re-using the rset between your two methods. So when countColumnsInTable has completed, the rset variable is pointing to a different result set than it was before, in listPosesInSection. And that result set has not been advanced with next, hence the error message you're getting. You probably want to assign it to a local ResultSet within that method instead.
public int countColumnsInTable(String sectionType) {
int count = 16;
try {
Statement statement = connection.createStatement();
ResultSet rset = statement.executeQuery("SELECT * FROM " + sectionType);
ResultSetMetaData rsMetaData = rset.getMetaData();
count = rsMetaData.getColumnCount();
} catch (SQLException e) {
e.printStackTrace();
}
// Remember to clean up
return count;
}
try this
public String listPosesInSection(String tableName, String sectionName) {
String strList;
StringBuilder strBuilderList = new StringBuilder("");
// Run the query
try {
statement = connection.createStatement();
// Query will collect all columns from one specific row
rset = statement.executeQuery("SELECT * FROM " + tableName + " WHERE " + tableName + "_name = '" + sectionName + "'");
while (rset.next()){
System.out.println("Column count is " + countColumnsInTable(tableName));
for (int i = 2; i <= countColumnsInTable(tableName); i++) {// First value (0) is always null, skip first column (1)
System.out.println("test");
strBuilderList.append(rset.getString(i) + "\n");
}
}
} catch (SQLException e) {
e.printStackTrace();
}
strList = strBuilderList.toString();
return strList.replaceAll(", $",""); // Strips off the trailing comma
}
rset is a ResultSet object
i think you are using the same ResultSet object in listPosesInSection and also in countColumnsInTable
so what is happening here is in listPosesInSection rset holds a result and you have also move the cursor but again in countColumnsInTable you are using the same rset so it is overwritten ie it holds a new result now, you are getting the number of columns but since it holds a new result now the cursor will be before the 1st record so use different Resultset object in countColumnsInTable