Java: Proper way to update TableModel's data? - java

This is a simplifed version of what I'm trying to do. I have a map which maps an integer id to a list of strings. One of these lists from the map is displayed by a JTable at all times. (Depending on which id needs to be displayed) All information for the map is coming from a database, and is constantly being added to and removed from.
My DataClass which stores the full map and receives the updates from the database:
DataClass {
Map(Integer, List<String>) map;
// TabelModel gets list here
public List<String> getList(int id) {
return map.get(id);
}
// Updates from database come here
public updateList(int id, String info) {
if (map.containsKey(id) {
map.get(id).add(info);
}
else {
map.put(id, new List<String>(info));
}
}
// Remove from list, etc all down here
...
}
My Table model class:
MyTableModel extends DefaultTableModel {
List data;
public void updateData(int id) {
data = getList(id)
fireTableDataChanged();
}
... All the other stuff needed ...
}
Since database updates occur in batches, I know that at the end of a batch I have to update the table. So the DataClass informs the UI class which informs the table to update. This causes the updateData(id) to be called which retrieves a new set of data from the DataClass and calls fireTableDataChanged();
My Question are:
Is this the right way to go about updating/storing the data in the table?
Should getList return a clone of the data? If I just return the reference all updates from the database would be accessing that same reference and since these updates aren't running in the EDT wouldn't that be bad/frowned upon/wrong?
How can I do this using Java Events? PropertyChangeEvent? If so, how?

To the extent that your questions are related,
No; when new data is available, your updateData() method should update the internal data structure of your TableModel and fire an appropriate event; because DefaultTableModel knows nothing of your List, extend AbstractTableModel, as shown here; the JTable will update itself in response.
No; your database access layer should retain no references to queried objects, and no cloning should be necessary; forward queried objects to your display layer from the process() method of SwingWorker, or similar.
Use a TableModelListener to learn when and how the TableModel has changed; update the database accordingly.

Related

How to refresh a JTable without setting a new table model?

I am trying to refresh a JTable using the DefaultTableModel without accessing the table itself again, but only the existing, but then updated table model.
Yet, I tried to update the table model itself and then notify the model about it (see in the code). For some reason, the table will not update. I do not know, if this is an access problem or if it is just not possible.
//in the Gui_Main class
private static void addTables(){
JTable tblMain = new JTable(Util_Tables.dtm);
}
//in the Util_Tables class, if the tables needs to be updated
public static DefaultTableModel dtm;
public static void updateTable(){
dtm = new DefaultTableModel(data, columns);
dtm.fireTableDataChanged();
}
So you're basic structure is all over the place. When you create a new instance of DefaultTableModel and assign it to dtm, this won't be reflected by the JTable, as it is still using the instance it first grabbed when it was created.
Exposing dtm the way you have, opens it up to undesirable modification and voids one of the principles of OO - encapsulation, where the class is responsible for the management of its properties. This is also a reason to reconsider the use of static
A better start would be to create a getter which returns a single instance of DefaultTableModel, so each call to it is guaranteed to return the same instance of DefaultTableModel and stops any one else from changing the underlying reference
private static void addTables(){
JTable tblMain = new JTable(Util_Tables.getModel());
}
//in the Util_Tables class, if the tables needs to be updated
private DefaultTableModel model;
public static DefaultTableModel getModel() {
if (model == null) {
model = new DefaultTableModel();
}
}
Okay, so how about updating the model? Well, you need to start by modifying your updateTable method, so it can be used to actually update the model in some meaningful way
public static void updateTable(Object[][] data, Object[] columnIdentifiers){
model.setDataVector(data, columnIdentifiers);
}
The model will then generate the events it needs itself. If you find yourself calling the fireXxx methods yourself, then it's a good indication that you're doing something wrong.

Detect objects to be deleted from Realm database

In my project I'm using Realm for storing data from API.
Before updating objects to Realm I'd like to check which objects are new (doesn't exist in database) and which objects should be deleted (exist in database, but don't exist in API response).
For checking new objects I'm iterating through API response and using simple Realm query to check which object is new
for(Follower follower: results.data){
Follower followerFromDb = realm.where(Follower.class).equalTo("id", follower.id).findFirst();
if(followerFromDb == null){
Log.d("REALM", "Object is not in the DB");
}
}
My problem is - how to efficiently check which objects should be deleted from the database.
I have a pretty nice trick for deleting objects not in API response, which is that I add an indexed field called #Index private boolean isBeingSaved; to my RealmObject:
public class Thingy extends RealmObject {
//...
#Index
private boolean isBeingSaved;
}
Then as I map the API response to RealmObjects, I set this to true:
ApiResponse apiResponse = retrofitService.getSomething();
Thingy thingy = new Thingy();
thingy.set/*...*/;
thingy.setIsBeingSaved(true);
realm.insertOrUpdate(thingy);
Afterwards, you've set each of these to true for the new elements. So you can do a deletion for all that is false.
realm.where(Thingy.class)
.equalTo(ThingyFields.IS_BEING_SAVED, false)
.findAll()
.deleteAllFromRealm();
Then you'll need to iterate the remaining objects and set their boolean field to false
for(Thingy thingy: realm.where(Thingy.class).findAll()) {
thingy.setIsBeingSaved(false);
}
And it works!
I do not know of a more optimized solution unfortunately, I can clearly see that this is O(N) because of iteration at the end. But you can follow https://github.com/realm/realm-java/issues/762 for bulk update support.
In your particular case, the special flag is isBeingSaved, and I guess you don't want to immediately delete them, but this is how I did it when I needed this functionality.
It sounds like you're database only contains data from the API, and local data is defunkt when an api call response is returned. If that's the case then you can simply delete everything in your database, and add everything from the api response into your Realm.
Realm realm = Realm.getDefaultInstance();
realm.executeTransaction(new Realm.Transaction() {
public void execute(Realm realm) {
realm.deleteAll(); //Delete everything
object.delete(); //Delete specific object
realm.delete(RealmModel) //Delete all of specific type
}
}
Remember to close your realm when you're done.

JavaFX: Handle changes in TableColumn

I have read this article about how to make TableColumn editable, I have made some modifications to obtain some reusable code as follows:
public static <T> void setEditableColumn(TableColumn<T, String> column, BiConsumer<T,String> modifiedField, String type){
column.setCellFactory(TextFieldTableCell.forTableColumn());
column.setOnEditCommit(event->{
T model = event.getRowValue();
if(!event.getNewValue().isEmpty()){
modifiedField.accept(model,event.getNewValue());
}
});
}
But I want for this method to return the modified Model.
Iit is clear that I retrieve the modified model inside a lambda expression what are the possible ways of doing this. My objective is to be able to save that model in a database.

A child class should maintain a collection, how to bind its scope to a parent's class method?

I have two classes that share the same flow: I need to get info from the DB, process it and update it on a nosql DB.
The difference is very small: in one case I am sure I will get only one entity for each nosql record, so I can just process them, store them temporarily in a list and then get all the items in the list after the entities were processed.
In the second case I can have more than one entity for each index record. In this case I have to retrieve the current processed data, merge, and store again. After every entity was processed I am able to get the map.values() so I can update to the nosql.
As seen the difference is only on how the items are processed and how the data structure is temporarily stored.
My parent class has something like this:
public void run()
{
process();
sendToNosql(getProcessed())
}
protected abstract void process();
protected abstract List<Stuff> getProcessed();
The simple children is something like this:
List<Stuff> myStuff = new ArrayList<>();
#Override
protected void process()
{
for(Entity e : loadFromDB()){
myStuff.add(process(e));
}
}
#Override
protected List<Stuff> getProcessed(){
return myStuff;
}
And the complex one:
Map<int, Stuff> myStuff = new HashMap<>();
#Override
protected void process()
{
for(Entity e : loadFromDB()){
myStuff.put(e.getId(), process(e));
}
}
#Override
protected List<Stuff> getProcessed(){
return myStuff.values();
}
Because everything will be processed and the results will be used later, myStuff is an object attribute. This works fine.
But because these are stateless beans, I am only safe while I am in the run() method, if any other method is called, the myStuff attribute is not reliable.
I would like to avoid this risk, so I would like to have the run() method as myStuff attribute scope. How can I improve my architecture to achieve this while abstracting the underlying data structure?
Or is this problem telling me I made something very wrong?
For information in case anyone finds this question:
I used the State pattern, so the child class generates a state when the process method starts. The state hides the way the information is stored (hash or list).

Getting hibernate persistent object at the time of pre update event

I am implementing pre update event listener in java hibernate 4.3.
I need to get old persistent object value before update occures.
I have tried using event.getOldState() in PreUpdateEventListener. But it gives Object[] as return type. I want the persistent object as return value.
How to get complete persistent object in preUpdateEvent?
The preUpdateEventListener is implemented correctly.
Just need to get Complete persisted object instead i get Object[].
Also tried event.getSession().get(id,persisted.class); //this gives new object as session has set new object to update
Below is code that gives Object[]
import org.hibernate.event.spi.PreUpdateEventListener;
import org.hibernate.event.spi.PreUpdateEvent;
public class MyEventListener implements PreUpdateEventListener {
public void onPreUpdate(PreUpdateEvent event) {
Object newEntity=event.getEntity(); //Gives new Object which will be updated.
Object[] oldEntity=evetn.getOldState(); //gives old Object[] which can't be converted to persisted Object
//Code here which will give me old persisted objects, hibernate fetches object in array format.
}
}
If i remember well the object array contains all attribute values of given entity :
the index of the associated property can be resolved using the property name array
String[] propertyNames = event.getPersister().getEntityMetamodel.getPropertyNames();
this link may be usefull
I am not sure how listeners work with pure Hibernate, but if you use JPA event listeners, the entity is passed as a parameter to the listener method:
public class MyUpdateListener {
#PreUpdate
public void onPreUpdate(MyEntiy e) {
e.getAttribute();
// do something
}
...
If you define a listener method inside the entity, you can simply access the state of this
#PreUpdate
public void onPreUpdate() {
getAttribute();
// do something
}

Categories