My CellTable does not sort - sorting

I red a lot about sorting a CellTable. I also went trough the ColumnSorting with AsyncDataProvider. But my CellTable does not sort.
Here is my code:
public class EventTable extends CellTable<Event> {
public EventTable() {
EventsDataProvider dataProvider = new EventsDataProvider(this);
dataProvider.addDataDisplay(this);
SimplePager.Resources pagerResources = GWT.create(SimplePager.Resources.class);
SimplePager pager = new SimplePager(TextLocation.CENTER, pagerResources, false, 5, true);
pager.setDisplay(this);
[...]
TextColumn<Event> nameCol = new TextColumn<Event>() {
#Override
public String getValue(Event event) {
return event.getName();
}
};
nameCol.setSortable(true);
AsyncHandler columnSortHandler = new AsyncHandler(this);
addColumnSortHandler(columnSortHandler);
addColumn(nameCol, "Name");
getColumnSortList().push(endCol);
}
}
public class EventsDataProvider extends AsyncDataProvider<Event> {
private final EventTable eventTable;
public EventsDataProvider(EventTable eventTable) {
this.eventTable = eventTable;
}
#Override
protected void onRangeChanged(HasData<Event> display) {
int start = display.getVisibleRange().getStart();
int length = display.getVisibleRange().getLength();
// check false values
if (start < 0 || length < 0) return;
// check Cache before making a rpc
if (pageCached(start, length)) return;
// get Events async
getEvents(start, length);
}
}
I do now know, if all the methods are need here. If so, I will add them. But in short:
pageCached calls a method in my PageCache Class which holds a map and a list. Before making a rpc call, the cache is checked if the events where already taken and then displayed.
getEvents just makes an rpc call via asynccallback which updates the rowdata via updateRowData() on success.
My Table is displayed fast with currently around 500 entries (could be more, depends on the customer). No missing data and the paging works fine.
I just cannot get the sorting work. As far as I know, AsyncHandler will fire a setVisibleRangeAndClearData() and then an onRangeChanged(). onRangeChanged is never fired. As for the setVisibleRangeAndClearData() I do not know. But the sortindicator (arrow next to the columnname) does change on every click.
I do not want to let the server sort the list. I have my own Comparators. It is enough, if the current visible page of the table is sorted. I do now want to sort the whole list.
Edit:
I changed following code in the EventTable constructor:
public EventTable() {
[...]
addColumnSortHandler(new ColumnSortEvent.AsyncHandler(this) {
public void onColumnSort(ColumnSortEvent event) {
super.onColumnSort(event);
MyTextColumn<Event> myTextColumn;
if (event.getColumn() instanceof MyTextColumn) {
// Compiler Warning here: Safetytype unchecked cast
myTextColumn = (MyTextColumn<Event>) event.getColumn();
MyLogger.log(this.getClass().getName(), "asc " + event.isSortAscending() + " " + myTextColumn.getName(), Level.INFO);
}
List<Event> list = dataProvider.getCurrentEventList();
if (list == null) return;
if (event.isSortAscending()) Collections.sort(list, EventsComparator.getComparator(EventsComparator.NAME_SORT));
else Collections.sort(list, EventsComparator.descending(EventsComparator.getComparator(EventsComparator.NAME_SORT)));
}
});
addColumn(nameCol, "Name");
getColumnSortList().push(endCol);
}
I had to write my own TextColumn to determine the Name of the column. Otherwise how should I know, which column was clicked? The page gets sorted now but I have to click twice on the column. After then, the sorting is done with every click but in the wrong order.
This solution does need polishing and it seems kinda hacky to me. Any better ideas?

The tutorial, that you linked to, states:
This sorting code is here so the example works. In practice, you would
sort on the server.
Async provider is used to display data that is too big to be loaded in a single call. When a user clicks on any column to sort it, there is simply not enough objects on the client side to display "first 20 evens by name" or whatever sorting was applied. You have to go back to your server and request these first 20 events sorted by name in ascending order. And when a user reverses sorting, you have to go to the server again to get first 20 events sorted by name in a descending order, etc.
If you can load all data in a single call, then you can use regular DataProvider, and all sorting can happen on the client side.
EDIT:
The problem in the posted code was in the constructor of EventsDataProvider. Now it calls onRangeChanged, and the app can load a new sorted list of events from the server.

Related

Querying single database row using rxjava2

I am using rxjava2 for the first time on an Android project, and am doing SQL queries on a background thread.
However I am having trouble figuring out the best way to do a simple SQL query, and being able to handle the case where the record may or may not exist. Here is the code I am using:
public Observable<Record> createRecordObservable(int id) {
Callable<Record> callback = new Callable<Record>() {
#Override
public Record call() throws Exception {
// do the actual sql stuff, e.g.
// select * from Record where id = ?
return record;
}
};
return Observable.fromCallable(callback).subscribeOn(Schedulers.computation());
}
This works well when there is a record present. But in the case of a non-existent record matching the id, it treats it like an error. Apparently this is because rxjava2 doesn't allow the Callable to return a null.
Obviously I don't really want this. An error should be only if the database failed or something, whereas a empty result is perfectly valid. I read somewhere that one possible solution is wrapping Record in a Java 8 Optional, but my project is not Java 8, and anyway that solution seems a bit ugly.
This is surely such a common, everyday task that I'm sure there must be a simple and easy solution, but I couldn't find one so far. What is the recommended pattern to use here?
Your use case seems appropriate for the RxJava2 new Observable type Maybe, which emit 1 or 0 items.
Maybe.fromCallable will treat returned null as no items emitted.
You can see this discussion regarding nulls with RxJava2, I guess that there is no many choices but using Optional alike in other cases where you need nulls/empty values.
Thanks to #yosriz, I have it working with Maybe. Since I can't put code in comments, I'll post a complete answer here:
Instead of Observable, use Maybe like this:
public Maybe<Record> lookupRecord(int id) {
Callable<Record> callback = new Callable<Record>() {
#Override
public Record call() throws Exception {
// do the actual sql stuff, e.g.
// select * from Record where id = ?
return record;
}
};
return Maybe.fromCallable(callback).subscribeOn(Schedulers.computation());
}
The good thing is the returned record is allowed to be null. To detect which situation occurred in the subscriber, the code is like this:
lookupRecord(id)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Record>() {
#Override
public void accept(Record r) {
// record was loaded OK
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) {
// there was an error
}
}, new Action() {
#Override
public void run() {
// there was an empty result
}
});

Intercept sorting the items of a JavaFX 8 TableView

I have an applications that hosts a TableView. Whenever the user sorts the rows by clicking the header of a particular column, I need to
Save the current order of items,
Do the actual sorting,
Save the new order of items.
I was able to spot this:
this.tableView.sortPolicyProperty().set(t -> {
System.out.println("saving source order");
... // Saving
FXCollections.sort(tableView.getItems(), t.getComparator());
System.out.println("saving target order");
... // Saving
return true;
});
However, this throws ClassCastException pretty often. Is there a better way of saving the item permutations before and after sorting?
You could listen to it using the ListChangeListener the better way :)
tv.getItems().addListener(new ListChangeListener<T>(){
#Override
public void onChanged(javafx.collections.ListChangeListener.Change<
? extends T> c) {
while(c.next()){
if(c.wasPermutated()){
System.out.println("is permuated");
}
}
}
});
Hope it helps.

How can I create temporary records of Linq-To-Sql types without causing duplicate key problems?

I have code that generates records based on my DataGridView. These records are temporary because some of them already exist in the database.
Crop_Variety v = new Crop_Variety();
v.Type_ID = currentCropType.Type_ID;
v.Variety_ID = r.Cells[0].Value.ToString();
v.Description = r.Cells[1].Value.ToString();
v.Crop = currentCrop;
v.Crop_ID = currentCrop.Crop_ID;
Unfortunately in this little bit of code, because I say that v.Crop = currentCrop,
now currentCrop.Crop_Varieties includes this temporary record. And when I go to insert the records of this grid that are new, they have a reference to the same Crop record, and therefore these temporary records that do already exist in the database show up twice causing duplicate key errors when I submit.
I have a whole system for detecting what records need to be added and what need to be deleted based on what the user has done, but its getting gummed up by this relentless tracking of references.
Is there a way I can stop Linq-To-Sql from automatically adding these temporary records to its table collections?
I would suggest revisiting the code that populates DataGridView (grid) with records.
And then revisit the code that operates on items from a GridView, keeping in mind that you can grab bound item from a grid row using the following code:
public object GridSelectedItem
{
get
{
try
{
if (_grid == null || _grid.SelectedCells.Count < 1) return null;
DataGridViewCell cell = _grid.SelectedCells[0];
DataGridViewRow row = _grid.Rows[cell.RowIndex];
if (row.DataBoundItem == null) return null;
return row.DataBoundItem;
}
catch { }
return null;
}
}
It is also hard to understand the nature of Crop_Variety code that you have posted. As the Crop_Variety seems to be a subclass of Crop. This leads to problems when the Crop is not yet bound to database and potentially lead to problems when you're adding Crop_Variety to the context.
For this type of Form application I normally have List _dataList inside form class, then the main grid is bound to that list, through ObjectBindingList or another way. That way _dataList holds all data that needs to be persisted when needed (user clicked save).
When you assign an entity object reference you are creating a link between the two objects. Here you are doing that:
v.Crop = currentCrop;
There is only one way to avoid this: Modify the generated code or generate/write your own. I would never do this.
I think you will be better off by writing a custom DTO class instead of reusing the generated entities. I have done both approaches and I like the latter one far better.
Edit: Here is some sample generated code:
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="RssFeed_RssFeedItem", Storage="_RssFeed", ThisKey="RssFeedID", OtherKey="ID", IsForeignKey=true, DeleteOnNull=true, DeleteRule="CASCADE")]
public RssFeed RssFeed
{
get
{
return this._RssFeed.Entity;
}
set
{
RssFeed previousValue = this._RssFeed.Entity;
if (((previousValue != value)
|| (this._RssFeed.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this._RssFeed.Entity = null;
previousValue.RssFeedItems.Remove(this);
}
this._RssFeed.Entity = value;
if ((value != null))
{
value.RssFeedItems.Add(this);
this._RssFeedID = value.ID;
}
else
{
this._RssFeedID = default(int);
}
this.SendPropertyChanged("RssFeed");
}
}
}
As you can see the generated code is establishing the link by saying "value.RssFeedItems.Add(this);".
In case you have many entities for wich you would need many DTOs you could code-generate the DTO classes by using reflection.

Using DataObjectTypeName in DataObjectSource

The functionality I am trying to use is:
- Create a ObjectDataSource for selection and updating controls on a web page (User Control).
- Use the DataObjectTypeName to have an object created that would send the data to an UpdateMethod.
- Before the values are populated in the DataObjectTypeName’s object, I would like to pre-populate the object so the unused items in the class are not defaulted to zeros and empty strings without me knowing whether the zero or default string was set by the user or by the application.
I cannot find a way to pre-populate the values (this was an issue back in 2006 with framework 2.0). One might ask “Why would anyone need to pre-populate the object?”. The simple answer is: I want to be able to randomly place controls on different User Controls and not have to be concerned with which UpdateMethod needs to handle which fields of an object.
For Example, let’s say I have a class (that reflects a SQL Table) that includes the fields: FirstName, LastName, Address, City, State, Zip. I may want to give the user the option to change the FirstName and LastName and not even see the Address, City, State, Zip (or vice-versa). I do not want to create two UpdateMethods where one handled FirstName and LastName and the other method handles the other fields. I am working with a Class of some 40+ columns from multiple tables and I may want some fields on one screen and not another and decide later to change those fields from one screen to another (which breaks my UpdateMethods without me knowing).
I hope I explained my issue well enough.
Thanks
This is hardly a solution to the problem, but it's my best stab at it.
I have a GridView with its DataSourceID set to an ObjectDataSource.
Whenever a row is updated, I want the property values in the object to be selectively updated - that is - only updated if they appear as columns in the GridView.
I've created the following extension:
public static class GridViewExtensions
{
public static void EnableLimitUpdateToGridViewColumns(this GridView gridView)
{
_gridView = gridView;
if (_gridView.DataSourceObject != null)
{
((ObjectDataSource)_gridView.DataSourceObject)
.Updating += new ObjectDataSourceMethodEventHandler(objectDataSource_Updating);
}
}
private static GridView _gridView;
private static void objectDataSource_Updating(object sender, ObjectDataSourceMethodEventArgs e)
{
var newObject = ((object)e.InputParameters[0]);
var oldObjects = ((ObjectDataSource)_gridView.DataSourceObject).Select().Cast<object>();
Type type = oldObjects.First().GetType();
object oldObject = null;
foreach (var obj in oldObjects)
{
if (type.GetProperty(_gridView.DataKeyNames.First()).GetValue(obj, null).ToString() ==
type.GetProperty(_gridView.DataKeyNames.First()).GetValue(newObject, null).ToString())
{
oldObject = obj;
break;
}
}
if (oldObject == null) return;
var dynamicColumns = _gridView.Columns.OfType<DynamicField>();
foreach (var property in type.GetProperties())
{
if (dynamicColumns.Where(c => c.DataField == property.Name).Count() == 0)
{
property.SetValue(newObject, property.GetValue(oldObject, null), null);
}
}
}
}
And in the Page_Init event of my page, I apply it to the GridView, like so:
protected void Page_Init()
{
GridView1.EnableLimitUpdateToGridViewColumns();
}
This is working well for me at the moment.
You could probably apply similar logic to other controls, e.g. ListView or DetailsView.
I'm currently scratching my head to think of a way this can be done in a rendering-agnostic manner - i.e. without having to know about the rendering control being used.
I hope this ends up as a normal feature of the GridView or ObjectDataSource control rather than having to hack it.

linqToSql related table not delay loading properly. Not populating at all

I have a couple of tables with similar relationship structure to the standard Order, OrderLine tables.
When creating a data context, it gives the Order class an OrderLines property that should be populated with OrderLine objects for that particular Order object.
Sure, by default it will delay load the stuff in the OrderLine property but that should be fairly transparent right?
Ok, here is the problem I have: I'm getting an empty list when I go MyOrder.OrderLines but when I go myDataContext.OrderLines.Where(line => line.OrderId == 1) I get the right list.
public void B()
{
var dbContext = new Adis.CA.Repository.Database.CaDataContext(
"<connectionString>");
dbContext.Connection.Open();
dbContext.Transaction = dbContext.Connection.BeginTransaction();
try
{
//!!!Edit: Imortant to note that the order with orderID=1 already exists
//!!!in the database
//just add some new order lines to make sure there are some
var NewOrderLines = new List<OrderLines>()
{
new OrderLine() { OrderID=1, LineID=300 },
new OrderLine() { OrderID=1, LineID=301 },
new OrderLine() { OrderID=1, LineID=302 },
new OrderLine() { OrderID=1, LineID=303 }
};
dbContext.OrderLines.InsertAllOnSubmit(NewOrderLines);
dbContext.SubmitChanges();
//this will give me the 4 rows I just inserted
var orderLinesDirect = dbContext.OrderLines
.Where(orderLine => orderLine.OrderID == 1);
var order = dbContext.Orders.Where(order => order.OrderID == 1);
//this will be an empty list
var orderLinesThroughOrder = order.OrderLines;
}
catch (System.Data.SqlClient.SqlException e)
{
dbContext.Transaction.Rollback();
throw;
}
finally
{
dbContext.Transaction.Rollback();
dbContext.Dispose();
dbContext = null;
}
}
So as far as I can see, I'm not doing anything particularly strange but I would think that orderLinesDirect and orderLinesThroughOrder would give me the same result set.
Can anyone tell me why it doesn't?
You're just adding OrderLines; not any actual Orders. So the Where on dbContext.Orders returns an empty list.
How you can still find the property OrderLines on order I don't understand, so I may be goofing up here.
[Edit]
Could you update the example to show actual types, especially of the order variable? Imo, it shoud be an IQueryable<Order>, but it's strange that you can .OrderLines into that. Try adding a First() or FirstOrDefault() after the Where.

Resources