HbnContainer property with id

Hi everybody,

I am new to the Vaadin Framework. I’m planning to use the HbnContainer for displaying table data.

By default HbnContainer does not contain the ID field of the hibernate entity. Is it possible to add a property with the ID field?

Thanks,
Daniel

Hi

It’s easy:

HbnContainer container = new HbnContainer(...)
container.addContainerProperty("id", Long.class, null)

Thank you for your reply. I didn’t think it is that simple.

But I have another question. Do you know how to change the order of the properties, so that the id property is shown as the first one?

Perhaps I just missed that, but I hope you can help me.

It’s easy too :slight_smile:

Table table = ...
HbnContainer container = ...
table.setContainerDataSource container
table.setVisibleColumns(new Object[]{"id", "name" ....})

After that table columns will appear exactly the same order as you specify.

That works, thanks.

But if I add the “id” property manually, then I’m not able to set a filter on that property. To implement a search functionality I have to filter the “id” property.

After adding the “id” property manually I’m also not able to sort the column in the table.

Is there any way to do that?

It seems to be impossible in current implementation.
HbnContainer uses Restrictions.ilike for filtering, so in many cases it wouldn’t be enough.

But you can easily extend the functionality of HbnContainer.
Addon jar contains source files, so you can correct it and recompile or just copy the source into your source tree (the way I did). Or you can extend your class from HbnContainer…

Here is what I did for my project:
I extended constructor with basic filtering criteria:

public HbnContainer(Class<T> entityType, Criterion baseQuery, SessionManager sessionMgr) {
        type = entityType;
        sessionManager = sessionMgr;
        this.baseQuery = baseQuery;
    }

You can use e.g. setter instead of constructor.

Next I have modified getBaseCriteria() method.

protected Criteria getBaseCriteria() {
        Criteria criteria = sessionManager.getSession().createCriteria(type);
        // Cusom code
        if (baseQuery!=null){
            criteria = criteria.add(baseQuery);
        }
        // End of cusom code
        // if container is filtered via Container.Filterable API
        if (filters != null) {

I’m not using container Filter API at all. All my filters are placed into baseQuery (it’s much more flexible)
Building base criteria is also easy, e.g:

Junction crit = Restrictions.conjunction()
crit = crit.add(Restrictions.eq("state", "DONE")
crit = crit.add(Restrictions.between("date", fromDate, toDate))
....
return crit;

Author of HbnContainer is also planning to add some extended filtering features, but I don’t know when.

I didn’t notice yet that sort are lost for ID property (and also for referenced objects). Thank you :slight_smile:
It will require additional container modification. I will investigate it.

Thanks a lot for your filtering extension, it works well!

Please let me know, if you have any solution for the sorting. That would be great :slight_smile:

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 :slight_smile:

post updated: fixed errors
note: orderPropertyIds.toString() should be orderPropertyIds[i]
.toString() - parser treats it as italic style marker :frowning:

Thanks for your solution. It works well with sorting the id property.

But I found two small mistakes in your example code, which made me struggle with some exceptions.

The dot in the split-regex in addSortedProperty() must be escaped, as it is a regular expression:

String[] props = hbnMapping.split("\\.");

And in getDefaultOrder() the index of the array is missing:

String orderPropertyId = orderPropertyIds.toString();

But the access on generated columns, eg. address.street, doesn’t yet work for me. The property with the dot cannot be resolved. It would be great if you could attach your complete example code, mainly the part where the container and the table are prepared. Perhaps I will find my mistake then.

Hi
Sorry for bugs. I was writing post in the same time while I refactor initial working version.
Grails cached working verion so I didn’t notice that it is broken.

Fixed version is in attachment (i’ve changed the package to org.vaadin… cause other addons are usually use it, and it is more comfortoble to lay all addons code together)
Please note that I can’t guarantee that I didn’t make any other changes to hbn code (I’ve tried to experiment with it and may not revert all the changes). But anyway it’s working.

Also I’ve updated initial post with code.
Third error was in addSortedProperty method: for complex orders “aliased” mapping should be stored
11299.java (56.7 KB)

Thanks. Now it’s working and has all the features I need.

I will test it in the next days, but it seems to work really well. Thank you for helping!

Hi I’m new too in using Vaadin. I will must use the hbnContainer but im enconter a problem on the java file HbnContainer. There are mmany error on the “Filter filter”. I noted that it wasn’t imported. Where was it?