Java JTable saving a single cell at a time to a database - java

I'm new at Java so this is probably an easy issue to a long time Java programmer.
Anyways, my issue is with using the JTable. I am wanting to load/save the data in that JTable. Problem being is that I am not sure how to go about saving a cell that the user has changed in some way.
The click event looks like this that I have:
#SuppressWarnings("serial")
class EditableTableModel extends AbstractTableModel {
String[] columnTitles;
Object[][] dataEntries;
int rowCount;
public void setValueAt(Object value, int row, int column) {
//Any change to the table
dataEntries[row][column] = value;
}
}
This function fires off just fine whenever I change any of the cells in the jTable. It also gives me the Row and the Column number from that edited cell along with, of course, the value that it was changed to. But my question is that:
How would I go about saving this data into my database if its just a cell value at a time?
My database structure looks like this (which has the same layout/order as the jTable structure):
ID | Data_script | data_status | data_users | data_error | data_rundate | lastUserWhoMod
-------------------------------------------------------------------------------------------
1 | Inquiries | Passed | Bob | No errors | 01/20/2019 | Bob
2 | Reporting | Passed | Jenny | No errors | 01/20/2019 | Bob
3 | Background | Failed | Bob | Lines 4,8 | 01/20/2019 | Jenny
4 | Maintenance | Passed | George | No errors | 01/20/2019 | Bob
So as an example, if I change the 1st row 3rd column which is data_users from Jenny to George then how would I map that to the database in order to save it in the correct spot (row, column)?

Need to consider also the table_key (pk assume id).
public void setValueAt(Object value, int row, int column) {
Object obj_key = dataEntries[row][0]; //assume id
//Any change to the table
dataEntries[row][column] = value;
//obj_key mapped inside method from Object to int
//get appropriate field_name from column index
run_sql(obj_key, row, column)
}
Raw update query
update table_name
set [computed_field_name] = column_new_value (dataEntries[row][column])
where id = obj_key
Since you know ahead corresponding names for cols, need also to set up properly
field_name ( if col=1 then data_script ...etc )

add a variable
int lastChanged = 0;
In setValueAt, add
lastChanged = row;
and call a function in a separate thread to update the database for the whole record, pointed out by lastChanged. That function must reset the value of lastChanged to -1.
The call to the DB must be performed in a separate thread, since setValueAt will be executing in the thread Java uses to paint the GUI's.

Related

Java - Gherkin & Cucumber: Pass an object or list of objects on a vertical table instead of horizontal

I have the following sample gherkin scenario on my feature file:
Scenario: Book an FX Trade
Given trades with the following details are created:
|buyCcy |sellCcy |amount |date |
|EUR |USD |12345.67 |23-11-2017 |
|GBP |EUR |67890.12 |24-11-2017 |
When the trades are executed
Then the trades are confirmed
In my glue file, I can map the data table to an object Trade as an out of the box cucumber solution:
#When("^trades with the following details are created:$")
public void trades_with_the_following_details_are_created(List<Trade> arg1) throws Throwable {
//do something with arg1
}
What I want to achieve:
Improve the readability of my gherkin scenario by doing the following:
Transpose the data table vertically, This will improve readability if my object has around 10 fields
Replace fields / column names with aliases
Sample:
Scenario: Book an FX Trade
Given trades with the following details are created:
|Buy Currency | EUR | GBP |
|Sell Currency | USD | EUR |
|Amount | 12345.67 | 67890.12 |
|Date | 23-11-2017 | 24-11-2017 |
When the trades are executed
Then the trades are confirmed
I want the table to be dynamic in a way that it can have more or less than 2 data sets / columns. What would be the best way to achieve this?
Additional info:
Language: Java 8
Cucumber version: 1.2.5
Trade POJO being something like:
public class Trade {
private String buyCcy;
private String sellCcy;
private String amount;
private String date;
/**
* These fields are growing and may have around 10 or more....
* private String tradeType;
* private String company;
*/
public Trade() {
}
/**
* accessors here....
*/
}
If the table is specified in your feature file as
|buyCcy | EUR | GBP |
|sellCcy | USD | EUR |
|amount | 12345.67 | 67890.12 |
|date | 23-11-2017 | 24-11-2017 |
you can use the following glue code (with your posted Trade class, assuming that there is a proper toString() method implemented)
#Given("^trades with the following details are created:$")
public void tradeWithTheFollowingDetailsAreCreated(DataTable dataTable) throws Exception {
// transpose - transposes the table from the feature file
// asList - creates a `List<Trade>`
List<Trade> list = dataTable.transpose().asList(Trade.class);
list.stream().forEach(System.out::println);
}
output
Trade{buyCcy=EUR, sellCcy=USD, amount=12345.67, date=23-11-2017}
Trade{buyCcy=GBP, sellCcy=EUR, amount=67890.12, date=24-11-2017}

How to pass Scenario out line data as a object in step method using cucumber-jvm

I am finding a solution to pass each scenario outline example row as object in cucuber-jvm.
So as for example if I consider a scenario
Scenario Outline: example
Given I have a url
When I choose <input_1>
Then page should hold field1 value as <validation field1> field2 value as <validation field2> fieldn value as <validation fieldn>
Examples:
| input_1 | validation field1 |validation field2|validation field n|
| input_1_case_1 | expected value 1 |expected value 1 |expected value n |
So in Step file
public void validationMethod(String validation field2,String validation field2,String validation field3){
............
............
}
So if I have more field then my method also consume more argument.
Now I want to pass all validation field as object in method. So is it possible using cucumber jvm? If possible could any one can please provide some suggestion with sample code.
You could try something like this
Then Use the following values
| <validation field1> | <validation field2> | <validation field3> |
Examples:
| input_1 | validation field1 |validation field2|validation field3 |
| input_1_case_1 | expected value 1 |expected value 2 |expected value 3 |
| input_2_case_2 | expected value 1 |expected value 2 |expected value 3 |
Step Definition
#Then("^Use the following values$")
public void useFollVal(List<String> valFields) {
//The values will be inside the list. Use index to access
}
You can even get an validation object instead of string list ie List<ValidationData>. To do this add a header in the step (not the examples table) with names matching the variables in the ValidationData class and cucumber will populate the data into the object.
Then Use the following values
| valField1 | valField2 | valField3 | <<<--- Header to add
| <validation field1> | <validation field2> | <validation field3> |
valField1 -> private String valField1; in ValidationData
Step Definition
#Then("^Use the following values$")
public void useFollVal(List<ValidationData> valObject) {
}
This is more of a comment: Wouldnt a variable length argument list work for you? You would need to know the sequence of your params though, without the argument names to help out.
public void multiParams(String... val){
}

Create dataframe from rdd objectfile

What is the method to create ddf from an RDD which is saved as objectfile. I want to load the RDD but I don't have a java object, only a structtype I want to use as schema for ddf.
I tried retrieving as Row
val myrdd = sc.objectFile[org.apache.spark.sql.Row]("/home/bipin/"+name)
But I get
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to
org.apache.spark.sql.Row
Is there a way to do this.
Edit
From what I understand, I have to read rdd as array of objects and convert it to row. If anyone can give a method for this, it would be acceptable.
If you have an Array of Object you only have to use the Row apply method for an array of Any. In code will be something like this:
val myrdd = sc.objectFile[Array[Object]]("/home/bipin/"+name).map(x => Row(x))
EDIT
you are rigth #user568109 this will create a Dataframe with only one field that will be an Array to parse the whole array you have to do this:
val myrdd = sc.objectFile[Array[Object]]("/home/bipin/"+name).map(x => Row.fromSeq(x.toSeq))
As #user568109 said there are other ways to do this:
val myrdd = sc.objectFile[Array[Object]]("/home/bipin/"+name).map(x => Row(x:_*))
No matters which one you will because both are wrappers for the same code:
/**
* This method can be used to construct a [[Row]] with the given values.
*/
def apply(values: Any*): Row = new GenericRow(values.toArray)
/**
* This method can be used to construct a [[Row]] from a [[Seq]] of values.
*/
def fromSeq(values: Seq[Any]): Row = new GenericRow(values.toArray)
Let me add some explaination,
suppose you have a mysql table grocery with 3 columns (item,category,price) and its contents as below
+------------+---------+----------+-------+
| grocery_id | item | category | price |
+------------+---------+----------+-------+
| 1 | tomato | veg | 2.40 |
| 2 | raddish | veg | 4.30 |
| 3 | banana | fruit | 1.20 |
| 4 | carrot | veg | 2.50 |
| 5 | apple | fruit | 8.10 |
+------------+---------+----------+-------+
5 rows in set (0.00 sec)
Now, within spark you want to read it, your code will be something like below
val groceryRDD = new JdbcRDD(sc, ()=> DriverManager.getConnection(url,uname,passwd), "select item,price from grocery limit ?,?",1,10,2,r => r.getString("item")+"|"+r.getString("price"))
Note :
In the above statement i converted the ResultSet into String r => r.getString("item")+"|"+r.getString("price")
So my JdbcRDD will be as
groceryRDD: org.apache.spark.rdd.JdbcRDD[String] = JdbcRDD[29] at JdbcRDD at <console>:21
now you save it.
groceryRDD.saveAsObjectFile("/user/cloudera/jdbcobject")
Answer to your question
while reading the object file you need to write as below,
val newJdbObjectFile = sc.objectFile[String]("/user/cloudera/jdbcobject")
In a blind manner ,just substitute the type Parameter of RDD you are saving.
In my case, groceryRDD has a type parameter as String, hence i have used the same
UPDATE:
In your case, as mentioned by jlopezmat, you need to use Array[Object]
Here each row of RDD will be Object, but since you have converted that using ObjectArray each row with its contents will be again saved as Array,
i.e, In my case , if save above RDD as below,
val groceryRDD = new JdbcRDD(sc, ()=> DriverManager.getConnection(url,uname,passwd), "select item,price from grocery limit ?,?",1,10,2,r => JdbcRDD.resultSetToObjectArray(r))
when i read the same using and collect data
val newJdbcObjectArrayRDD = sc.objectFile[Array[Object]]("...")
val result = newJdbObjectArrayRDD.collect
result will be of type Array[Array[Object]]
result: Array[Array[Object]] = Array(Array(raddish, 4.3), Array(banana, 1.2), Array(carrot, 2.5), Array(apple, 8.1))
you can parse the above based on your column definitions.
Please let me know if it answered you question

Changing Mocked ResultSet value each time a SQL statement is run

I'm using Mockrunner to create a mock result set for a select statement. I have a loop that executes the select statement (which returns a single value). I want to have the result set return a different value each time, but I have been unable to find anything about how to specify the result set return value based on the times the statement has been called. Here's a pseudocode snippet of the code:
In the test Code:
String selectSQL = "someselectStmt";
StatementResultSetHandler stmtHandler = conn.GetStatementResultSetHandler();
MockResultSet result = stmtHandler.createResultSet();
result.addRow(new Integer[]{new Integer(1)});
stmtHandler.prepareResultSet(selectSQL, result);
In the Actual Target Class:
Integer[] Results = getResults(selectSQL);
while(Results.length != 0){
//do some stuff that change what gets returned in the select stmt
Results = getResults(selectSQL)
}
So essentially I'd like to return something like 1 on the first time through, 2 on the 2nd and nothing on the 3rd. I haven't found anything so far that I'd be able to leverage that could achieve this. The mocked select statement will always return whatever the last result set was to be associated with it (for instance if I created two MockResultSets and associated both with the same select stmt). Is this idea possible?
Looping Control Flow Working Within Java and SQL
If you're coding this one in Java, a way to make your code execution calls return different, sequenced results can be accomplished throughh a looping control flow statement such as a do-while-loop. This Wikipedia reference has a good discussion using the contrast of the do-while-loop between implementations in Java and also in different programming lanugages.
Some Additional Influences through Observation:
A clue from your work with the Mockrunner tool:
The mocked select statement will always return whatever the last result set was to be associated with it (for instance if I created two MockResultSets and associated both with the same select stmt)
This is the case because the SELECT statement must actually change as well or else repeating the query will also repeat the result output. A clue is that your SQL exists as a literal string value throughout the execution of the code. Strings can be altered through code and simple string manipulations.
String selectSQL = "someselectStmt";
StatementResultSetHandler stmtHandler = conn.GetStatementResultSetHandler();
MockResultSet result = stmtHandler.createResultSet();
result.addRow(new Integer[]{new Integer(1)});
stmtHandler.prepareResultSet(selectSQL, result);
in addition to the selectSQL variable, also add a line for a numeric variable to keep track of how many times the SQL statement is executed:
Int queryLoopCount = 0;
In the following target class:
Integer[] Results = getResults(selectSQL);
while(Results.length != 0){
//do some stuff that change what gets returned in the select stmt
Results = getResults(selectSQL)
}
Try rewriting this WHILE loop control following this example. In your pseudocode, you will keep pulling the same data from the call to getResults(selectSQL); because the query remains the same through every pass made through the code.
Setting up the Test Schema and Example SQL Statement
Here is a little workup using a single MySQL table that contains "testdata" output to be fed into some result set. The ID column could be a way of uniquely identifying each different record or "test case"
SQL Fiddle
MySQL 5.5.32 Schema Setup:
CREATE TABLE testCaseData
(
id int primary key,
testdata_col1 int,
testdata_col2 varchar(20),
details varchar(30)
);
INSERT INTO testCaseData
(id, testdata_col1, testdata_col2, details)
VALUES
(1, 2021, 'alkaline gab', 'First Test'),
(2, 322, 'rebuked girdle', '2nd Test'),
(3, 123, 'municipal shunning', '3rd Test'),
(4, 4040, 'regal limerick', 'Skip Test'),
(5, 5550, 'admonished hundredth', '5th Test'),
(6, 98, 'docile pushover', '6th Test'),
(7, 21, 'mousiest festivity', 'Last Test');
commit;
Query 1 A Look at All the Test Data:
SELECT id, testdata_col1, testdata_col2, details
FROM testCaseData
Results:
| ID | TESTDATA_COL1 | TESTDATA_COL2 | DETAILS |
|----|---------------|----------------------|------------|
| 1 | 2021 | alkaline gab | First Test |
| 2 | 322 | rebuked girdle | 2nd Test |
| 3 | 123 | municipal shunning | 3rd Test |
| 4 | 4040 | regal limerick | Skip Test |
| 5 | 5550 | admonished hundredth | 5th Test |
| 6 | 98 | docile pushover | 6th Test |
| 7 | 21 | mousiest festivity | Last Test |
Query 2 Querying Only the First Record in the Table:
SELECT id, testdata_col1, testdata_col2, details
FROM testCaseData
WHERE id = 1
Results:
| ID | TESTDATA_COL1 | TESTDATA_COL2 | DETAILS |
|----|---------------|---------------|------------|
| 1 | 2021 | alkaline gab | First Test |
Query 3 Querying a Specific Test Record Within the Table:
SELECT id, testdata_col1, testdata_col2, details
FROM testCaseData
WHERE id = 2
Results:
| ID | TESTDATA_COL1 | TESTDATA_COL2 | DETAILS |
|----|---------------|----------------|----------|
| 2 | 322 | rebuked girdle | 2nd Test |
Query 4 Returning and Limiting the Output Set Size:
SELECT id, testdata_col1, testdata_col2, details
FROM testCaseData
WHERE id < 5
Results:
| ID | TESTDATA_COL1 | TESTDATA_COL2 | DETAILS |
|----|---------------|--------------------|------------|
| 1 | 2021 | alkaline gab | First Test |
| 2 | 322 | rebuked girdle | 2nd Test |
| 3 | 123 | municipal shunning | 3rd Test |
| 4 | 4040 | regal limerick | Skip Test |
Writing a Parameterized SQL Statement
I do not know if this difference in syntax yields the exact same results as your pseudocode, but I am recommending it from references of code structures that I know already work.
set condition value before loop
do{
// do some work
// update condition value
}while(condition);
The WHILE condition is instead at the end of the statement and should be based on a change to a value made within the looping block. We will now introduce the second variable, an int which tracks the number of times that the loop is iterated over:
String selectSQL = "someselectStmt";
String[] Results; = getResults(selectSQL);
// set condition value before loop
queryLoopCount = 0
do{
// do some work
Results = getResults(selectSQL);
// update condition value
queryLoopCount = queryLoopcount + 1;
}while(queryLoopCount < 6);
Where selectSQL comes from:
SELECT id, testdata_col1, testdata_col2, details
FROM testCaseData
WHERE id = 2;
And adapts with a built in parameter to:
selectSQL = 'SELECT id, testdata_col1, testdata_col2, details
FROM testCaseData
WHERE id = ' + queryLoopCount;
Mixing the string and integer values may not be a problem as in this reference on concatenated(+) values suggests: Anything concatenated to a string is converted to string (eg, "weight = " + kilograms).
Ideas for Specialized Case Requirements
You could introduce your own numbering sequence to get the records of each case to cycle through the reference table. There are a lot of possibilities by introducing an ORDER BY statement and altering the key ORDER BY value.
The "Skip" case. Within the Do-While loop, add a IF-THEN statement to conditionally skip a specific record.
set condition value before loop
do{
if ( queryLoopCount <> 4 ) {
// do some work}
// update condition value
queryLoopCount = queryLoopCount + 1;
}while(condition);
Using an if-then loop, this code sample will process all test records but will skip over the record of ID = 4 and continue through until the while loop condition is met.

What is the correct way to use db values in code

I've in a lot of places in my code hard coded comparing and I'm not happy with that. I'm looking for the correct way to approach this.
EXAMPLE
public class Status {
public static final int ACTIVE = 1,
INACTIVE = 2,
ENDED = 3,
PAUSED = 4,
NEW = 5,
INIT = 6,
STARTED = 7;
private int id;
private String name;
public int getId(){ return this.id; }
public String getName(){ return this.name; }
//get and set the object from the db by the id
public Status(int id){}
}
public class Job {
private int id;
private Status stage;
//get and set the object from the db by the id
Job(int id){}
public boolean isStageStatusEnded(){
return this.stage.getId() == Status.ENDED;
}
}
I've this DB table:
mysql> desc statuses;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(20) | NO | | NULL | |
+-------+-------------+------+-----+---------+----------------+
mysql> select * from statuses;
+----+----------+
| id | name |
+----+----------+
| 1 | ACTIVE |
| 2 | INACTIVE |
| 3 | ENDED |
| 4 | PAUSED |
| 5 | NEW |
| 6 | INIT |
| 7 | STARTED |
+----+----------+
As you can see the static final int in Status class is exact copy of the table statuses and that for the return this.stage.getId() == Status.ENDED; line. Now if in any time the values will change(id/name) i'll have to change the static int's as well. I dont see how can I change it but if you know a way - share it.
There is several ways. This can be one of them:
Drop the final keyword from your constants.
On start the application, query to the database for the current values.
Populate the values in the constants with Java reflection.
Field field = Status.class.getDeclaredField(name);
field.setInt(field, id);
You'll still have to keep these values hardcoded somewhere, either in the database or in the code cause otherwise you will simply not know what each value stands for. After that you can either persist them to the DB if that suits your needs or load into your app.
As Paul wrote, you'd load the values from DB when your application starts, but I suggest using a enum instead of a number of int constants which may save a lot of nerve.
Here is a link you may find useful and as for enums over constants, read J.Bloch, Effective Java.

Categories

Resources