And again extending sorting in HbnContainer (just hbn later) was quite easy.
Lets start with example entities:
class Person{
long id
String name
int age
Address address
...
}
class Address{
long id
String street
String city
}
Assume we are using hbn for Person.
By default hbn allows you to sort by any property, except ID: name, age, address.
For address, column will be sorted by address id’s.
In the Table, to render correctly address column we have to use generated column (if we don’t want to rely on Address#toString):
table.addGeneratedColumn("addressStreet", {
Table source, Object itemId, Object columnId ->
Person person = source.getItem(itemId).getPojo()
new Label(person.address.street)
}as ColumnGenerator)
It’s groovy syntax (to be more compact). Note that column name “addressStreet” is absent in hbn (it’s not object property)
So I want to be able to sort not only by Person#id, but by Person#address.street
The idea behind container extention is to add ability to specify any column name as sortable (even complex)
Adding method:
public void addSortedProperty(String propertyId, String hbnMapping)
Which will allow as to provide additional sortable columns. PropertyId is property id in container (including names of generated Table columns) and hbnMapping is sorting property in entity, e.g.:
HbnContainer hbn = new HbnContainer(Person.class ...
hbn.addContainerProperty("id", Long.class, null)
hbn.addSortedProperty("id", "id")
hbn.addSortedProperty("addressStreet", "address.street")
// it is important to add this mapping before assigning to table
table.setContainerDataSource hbn
To implement this extending hbn:
...
// mapping: table column -> sorting property
private Map<String, String> additionalSortedProperties = new HashMap<String, String>();
// mapping table column -> alias name
private Map<String, String> additionalSortedPropertiesAliases = new HashMap<String, String>();
// mapping alias name -> porperty name
private Map<String, String> additionalSortedPropertiesAliasesMapping = new HashMap<String, String>();
...
public void addSortedProperty(String propertyId, String hbnMapping) {
if (hbnMapping.indexOf(".") > 0) {
// complex property
String[] props = hbnMapping.split("\\.");
if (props.length != 2) { //e.g. mapping like address.street.name is not allowed
throw new IllegalArgumentException("Invalid order mapping (only second level entities are allowed): " + hbnMapping);
}
// alias is a property name without ".": address.street-> addressstreet
String alias = props[0]
+ props[1]
;
while (additionalSortedPropertiesAliases.containsValue(alias)) {
alias = alias + "1";
}
additionalSortedPropertiesAliases.put(propertyId, alias);
additionalSortedPropertiesAliasesMapping.put(alias, props[0]
);
additionalSortedProperties.put(propertyId, alias+"."+props[1]
);
} else {
additionalSortedProperties.put(propertyId, hbnMapping);
}
}
...
// extending to disable double ordering by ID if ID order selected
protected final List<Order> getOrder(boolean flipOrder) {
List<Order> orders = new ArrayList<Order>();
// standard sort order set by the user by property
orders.addAll(getDefaultOrder(flipOrder));
// custom code
// looking if current order includes ID ordering
boolean explicitOrderById = false;
if (orderPropertyIds != null) {
for (Object str : orderPropertyIds) {
if (str != null && str.equals(getIdPropertyName())) {
explicitOrderById = true;
break;
}
}
}
if (!explicitOrderById) {
// natural order
orders.add(getNaturalOrder(flipOrder));
}
// end of custom code
return orders;
}
...
// extending to add correct orders to query (remapped from Table property names)
protected List<Order> getDefaultOrder(boolean flipOrder) {
List<Order> orders = new ArrayList<Order>();
if (orderPropertyIds != null) {
for (int i = 0; i < orderPropertyIds.length; i++) {
// changed part
String orderPropertyId = orderPropertyIds[i]
.toString();
if (additionalSortedProperties.containsKey(orderPropertyId)) {
// remapping custom column name to hbn property
orderPropertyId = additionalSortedProperties.get(orderPropertyId);
} else {
if (propertyInEmbeddedKey(orderPropertyId)) {
String idName = getClassMetadata()
.getIdentifierPropertyName();
orderPropertyId = idName + "." + orderPropertyId;
}
}
// end of changed part
boolean a = flipOrder ? !orderAscendings[i]
: orderAscendings[i]
;
...
....
// extending to add aliases to criteria (without aliases order on 2nd level properties is impossible)
protected Criteria getBaseCriteria() {
Criteria criteria = sessionManager.getSession().createCriteria(type);
if (baseQuery != null) {
criteria = criteria.add(baseQuery);
}
// custom code
if (orderPropertyIds != null) {
for (Object obj : orderPropertyIds) {
String prop = (String) obj;
if (prop != null && additionalSortedPropertiesAliases.containsKey(prop)) {
String alias = additionalSortedPropertiesAliases.get(prop);
criteria.createAlias(additionalSortedPropertiesAliasesMapping.get(alias), alias);
}
}
}
// end of custom code
// if container is filtered via Container.Filterable API
if (filters != null) {
...
...
// extending to provide "host" table complete info on sortable columns
public Collection<String> getSortableContainerPropertyIds() {
// use Hibernates metadata helper to determine property names
String[] propertyNames = getClassMetadata().getPropertyNames();
Set<String> propertyIds = new HashSet<String>(); // changed to set
propertyIds.addAll(Arrays.asList(propertyNames));
propertyIds.addAll(getEmbeddedKeyPropertyIds());
propertyIds.addAll(additionalSortedProperties.keySet()); // adding our custom properties
return propertyIds;
}
Its working, but I didn’t test it deeply. Any thoughts are welcome
post updated: fixed errors
note: orderPropertyIds.toString() should be orderPropertyIds[i]
.toString() - parser treats it as italic style marker