TableModel, TableFacade, Best Practices

99 views
Skip to first unread message

Sam Welter

unread,
Jan 27, 2012, 5:09:33 PM1/27/12
to jmesa_forum
Hi there - we have been using DisplayTag for the last 5 years, and we
are working on migrating to jmesa for all data tables across our
(large) application. The application I work on has a oracle/hibernate/
spring mvc stack, with some database work done in jdbc for performance
reasons. I'm struggling to implement pagination using TableModel,
which appears to be the 'best practice' for jmesa 3.x.

Following your example, I wrote the following:

//caseTable extends TableModel
caseTable.setItems(new PageItems() {
public int getTotalRows(Limit limit) {
TableSearchFilter filter = getTableFilter(limit);
return
caseSearchService.getCasesCountWithFilter(filter);
}

public Collection<?> getItems(Limit limit) {
TableSearchFilter tableFilter = getTableFilter(limit);
TableSort tableSort = getTableSort(limit);
int rowStart = limit.getRowSelect().getRowStart();
int rowEnd = limit.getRowSelect().getRowEnd();
return
caseSearchService.getCasesWithFilterAndSort(tableFilter, tableSort,
rowStart, rowEnd);
}
});

Basically, the example copies values from the Limit object into
buildCriteria() to create Criteria for the hibernate query. The
problem is, since PageItems requires the Limit object, this doesn't
give much flexibility to the developer as far as using HQL/jdbc for
complicated queries and nested objects.

Any advice? Should I resort to using TableFacade? I don't really want
to do that - I like the simpler, cleaner TableModel object, and it
sounds like that is the direction you want to take jmesa in. Is there
any thought of exposing a few more methods in TableModel to support
pagination?

Jeff Johnston

unread,
Jan 27, 2012, 11:24:02 PM1/27/12
to jmesa...@googlegroups.com
The problem is, since PageItems requires the Limit object, this doesn't
give much flexibility to the developer as far as using HQL/jdbc for
complicated queries and nested objects.

The idea behind the PageItems is that it will calculate the Limit for you. You just have to use the information inside it to fetch a page of data. The Limit is handed to you in the PageItems callback so I am wondering what you mean by it doesn't give much flexibility? It is just supposed to represent what the user did on the page (filter and sort), and take the total count that you supply to figure out what page to return.

The TableFacade is considered the lower level API and I cannot really think of a reason to use it directly anymore.

Can you explain a little more about what you would need?

-Jeff

Sam Welter

unread,
Jan 30, 2012, 4:12:49 PM1/30/12
to jmesa_forum
Basically what I'm trying to do is take this of the logic out of the
controller. I guess my question is simple: how do I pass the Limit to
a service method, rather than implement the PageItems interface on the
fly in the controller? I don't quite understand how limit is
populated.

//instead of this:
tableModel.setItems(new PageItems() {
public int getTotalRows(Limit limit) {
PresidentFilter presidentFilter =
getPresidentFilter(limit);
return
presidentService.getPresidentsCountWithFilter(presidentFilter);
}

public Collection<?> getItems(Limit limit) {
PresidentFilter presidentFilter =
getPresidentFilter(limit);
PresidentSort presidentSort = getPresidentSort(limit);
int rowStart = limit.getRowSelect().getRowStart();
int rowEnd = limit.getRowSelect().getRowEnd();
return
presidentService.getPresidentsWithFilterAndSort(presidentFilter,
presidentSort, rowStart, rowEnd);
}
});

I'd like to do this:

tableModel.setItems(presidentService.getPaginatedResults(limit));

where getPaginatedResults() (or whatever I call it) returns an object
that implements the PageItems interface.
too many joins, and I already have the my query to count records
already written in HQL.

On Jan 27, 10:24 pm, Jeff Johnston <jeff.johnston...@gmail.com> wrote:
> > The problem is, since PageItems requires the Limit object, this doesn't
> > give much flexibility to the developer as far as using HQL/jdbc for
> > complicated queries and nested objects.
>
> The idea behind the PageItems is that it will calculate the Limit for you.
> You just have to use the information inside it to fetch a page of data. The
> Limit is handed to you in the PageItems callback so I am wondering what you
> mean by it doesn't give much flexibility? It is just supposed to represent
> what the user did on the page (filter and sort), and take the total count
> that you supply to figure out what page to return.
>
> The TableFacade is considered the lower level API and I cannot really think
> of a reason to use it directly anymore.
>
> Can you explain a little more about what you would need?
>
> -Jeff
>
> > pagination?- Hide quoted text -
>
> - Show quoted text -

Sam Welter

unread,
Jan 31, 2012, 1:49:08 PM1/31/12
to jmesa_forum
Hi again - very sorry my message from yesterday is a bit incoherent.
In my defense, I was working on very little sleep!

The basic question remains the same - is there a way to access the
Limit object and pass it to my own service method?

I want to do this for two reasons. First, I already have much of the
search and sort code written in HQL from our DisplayTag days. It's
pretty complex,with lots of joins, and I haven't been able to reverse
engineer it into the elegant buildCriteria() method that you have in
your example. Second, I'm thinking of "advanced search" screens. I
want to use the same method to return results to the tableModel,
regardless of the type of search (Jmesa filters or a standard html
form-based submission).

Does that make my problem more clear?
> > - Show quoted text -- Hide quoted text -

Jeff Johnston

unread,
Jan 31, 2012, 3:09:27 PM1/31/12
to jmesa...@googlegroups.com
I think I get why you want to just pass the Limit into your service as it would be cleaner. The PageItems keeps the Limit tied closer to the controller code. However, the reason for the callback is so that you do not have to deal with creating the Limit. The reason the Limit can be tricky to create is that you first need to get the total rows count and then use that count to get the pages. Technically I think the only reason we need the total rows is so we can display it to the user on the screen...but people always seem to want to see the total rows count so I baked it right into the Limit RowSelect creation.

You can see what the getItems() does by looking at the utils class.

http://code.google.com/p/jmesa/source/browse/trunk/jmesa/src/org/jmesa/model/TableModelUtils.java

You could go the other way though and inject your service into an class that implements the PageItems and make it a little cleaner. Or just make two calls to your service class (one for the total rows and one for the items themselves) and pass the Limit in each time. Either way using the PageItems does not cut down on your flexibility, but it does mean that you have to work within the callback.

Does it make sense on why we have to get the total rows in a separate call?

Feel free to keep pushing back though...I like talking about designs. Just not seeing the technical limitations to using the PageItems.

-Jeff Johnston

Sam Welter

unread,
Feb 1, 2012, 11:02:33 AM2/1/12
to jmesa_forum
What I've done for now is to implement a PageItems interface in my
service tier and return that to the controller. But the downside is
that I am now tied to Jmesa in my service layer, and my betters always
tell me to aim for loose coupling. Also too - if I was to change my
approach and code it the way you have it in the example (implementing
PageItems on the fly directly in the controller), I would have to add
the same 14 lines of code to every single controller (almost 200 and
counting) and change two lines (the database calls). Copy and paste is
another indicator of poor design.

On the other hand, if I had access to the Limit object, I could pass
the name/value pairs of the sort and filter(s) to the DAOs via the
service layer, and return the record count and the result set to the
tableModel, that seems like a better design to me. But maybe I'm
missing something.

All that said, I like Jmesa a lot - I think it's really cool. Thanks
for all your work on it, and for your help!

Sam

On Jan 31, 2:09 pm, Jeff Johnston <jeff.johnston...@gmail.com> wrote:
> I think I get why you want to just pass the Limit into your service as it
> would be cleaner. The PageItems keeps the Limit tied closer to the
> controller code. However, the reason for the callback is so that you do not
> have to deal with creating the Limit. The reason the Limit can be tricky to
> create is that you first need to get the total rows count and then use that
> count to get the pages. Technically I think the only reason we need the
> total rows is so we can display it to the user on the screen...but people
> always seem to want to see the total rows count so I baked it right into
> the Limit RowSelect creation.
>
> You can see what the getItems() does by looking at the utils class.
>
> http://code.google.com/p/jmesa/source/browse/trunk/jmesa/src/org/jmes...
>
> You could go the other way though and inject your service into an class
> that implements the PageItems and make it a little cleaner. Or just make
> two calls to your service class (one for the total rows and one for the
> items themselves) and pass the Limit in each time. Either way using the
> PageItems does not cut down on your flexibility, but it does mean that you
> have to work within the callback.
>
> Does it make sense on why we have to get the total rows in a separate call?
>
> Feel free to keep pushing back though...I like talking about designs. Just
> not seeing the technical limitations to using the PageItems.
>
> -Jeff Johnston
>

Jeff Johnston

unread,
Feb 1, 2012, 12:39:59 PM2/1/12
to jmesa...@googlegroups.com
I would for sure not implement the PageItems in your service tier. If you are only changing two lines there will be quite a few ways that you could go about abstracting that out.

Glad that you like JMesa! Development starts up again in the next few weeks...I have some new requirements that I need to implement. I plan on starting by removing the deprecated methods and want to make the export ViewExporter more pluggable (like the rest of the framework). Then on to some enhancements. No breaking changes though so it will be very seamless.

-Jeff

Sam Welter

unread,
Feb 3, 2012, 2:50:01 PM2/3/12
to jmesa_forum
Ok - after consulting with the team, and seeing your recommendation, I
changed my implementation and removed PageItems from my service layer.
So that's solved for now.

Sadly, I'm still struggling to get the filters working correctly! They
work perfectly well for String fields, but the date fields I get a
type mismatch error (stack trace below). I've tried adding a
filterMatcher directly, and using the filterMatcherMap, but neither
seem to work, in spite of cribbing from your example almost verbatim.
Do I need to do anything more than this?

//add filterMatcher to tableModel
tableModel.addFilterMatcher(new MatcherKey(Date.class, "dueDate"), new
DateFilterMatcher("MM/dd/yyyy"));
...

//in the filter object that extends criteria command...
private void buildCriteria(Criteria criteria, String property, Object
value) {
if (value != null) {
criteria.add(Restrictions.like(property, "%" + value +
"%").ignoreCase());
}
}

//in the DAO -
public List<CaseDetail> getCasesWithFilterAndSort(CriteriaFilter
criteriaFilter, CriteriaSort criteriaSort, int rowStart, int rowEnd) {
List cases = (List) getHibernateTemplate().execute(new
HibernateCallback() {
public Object doInHibernate(Session session) throws
HibernateException, SQLException {
Criteria criteria =
session.createCriteria(CaseDetail.class);
criteria = criteriaFilter.execute(criteria);
criteria = criteriaSort.execute(criteria);
criteria.setFirstResult(rowStart);
criteria.setMaxResults(rowEnd - rowStart);
return criteria.list();
}
});
return cases;
}

Any thoughts, anything you can point me to as the possible issue..?
Again, thanks for all your help.

Stack Trace:
Caused by: java.lang.ClassCastException: java.lang.String
at
org.hibernate.type.descriptor.java.JdbcDateTypeDescriptor.unwrap(JdbcDateTypeDescriptor.java:
40)
at org.hibernate.type.descriptor.sql.DateTypeDescriptor
$1.doBind(DateTypeDescriptor.java:53)
at
org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:
91)
at
org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:
283)
at
org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:
278)
at org.hibernate.loader.Loader.bindPositionalParameters(Loader.java:
1873)
at org.hibernate.loader.Loader.bindParameterValues(Loader.java:1844)
at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:
1716)
at org.hibernate.loader.Loader.doQuery(Loader.java:801)
at
org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:
274)
at org.hibernate.loader.Loader.doList(Loader.java:2542)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2276)
at org.hibernate.loader.Loader.list(Loader.java:2271)
at
org.hibernate.loader.criteria.CriteriaLoader.list(CriteriaLoader.java:
119)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1716)
at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:347)
at org.hibernate.impl.CriteriaImpl.uniqueResult(CriteriaImpl.java:
369)




On Feb 1, 11:39 am, Jeff Johnston <jeff.johnston...@gmail.com> wrote:
> I would for sure not implement the PageItems in your service tier. If you
> are only changing two lines there will be quite a few ways that you could
> go about abstracting that out.
>
> Glad that you like JMesa! Development starts up again in the next few
> weeks...I have some new requirements that I need to implement. I plan on
> starting by removing the deprecated methods and want to make the export
> ViewExporter more pluggable (like the rest of the framework). Then on to
> some enhancements. No breaking changes though so it will be very seamless.
>
> -Jeff
>

Jeff Johnston

unread,
Feb 3, 2012, 3:03:56 PM2/3/12
to jmesa...@googlegroups.com
If you are using the PageItems to get just one page of data then you are handling the filter and sort manually. The only job JMesa has is giving you what the user wants filtered and sorted in the Limit object.

In fact you should just turn off the filtering and sorting completely by setting the autoFilterAndSort() to false. Really this should happen automatically when using the PageItems but I think I had an edge case that needed it (should probably revisit why I have this).

tableModel.autoFilterAndSort(false);


The FilterMatcher is only useful if you want JMesa to do the filtering and sorting.

-Jeff

Sam Welter

unread,
Feb 5, 2012, 7:47:14 PM2/5/12
to jmesa_forum
Good to know about the FilterMatcher, but I am handling the filter and
sort manually. The problem I was trying to describe is when I try to
filter my results using non-String values.

Let me restate the question: in the example, this method is used -

protected PresidentFilter getPresidentFilter(Limit limit) {
PresidentFilter presidentFilter = new PresidentFilter();
FilterSet filterSet = limit.getFilterSet();
Collection<Filter> filters = filterSet.getFilters();
for (Filter filter : filters) {
String property = filter.getProperty();
String value = filter.getValue();
presidentFilter.addFilter(property, value);
}
return presidentFilter;
}

and the presidentFilter is passed to the DAO to create the query using
criteria.add() (on line 54 in PresidentFilter.java). In my
implementation, which follows this example closely, when I filter the
Date field, the query throws a type mismatch error. How is the filter
on the page correctly bound to the query as a Date? It's being passed
as an Object via presidentFilter, and Hibernate parses it as a String.
Is the code that actually runs the examples different?



On Feb 3, 2:03 pm, Jeff Johnston <jeff.johnston...@gmail.com> wrote:
> If you are using the PageItems to get just one page of data then you are
> handling the filter and sort manually. The only job JMesa has is giving you
> what the user wants filtered and sorted in the Limit object.
>
> In fact you should just turn off the filtering and sorting completely by
> setting the autoFilterAndSort() to false. Really this should happen
> automatically when using the PageItems but I think I had an edge case that
> needed it (should probably revisit why I have this).
>
> tableModel.autoFilterAndSort(false);
>
> The FilterMatcher is only useful if you want JMesa to do the filtering and
> sorting.
>
> -Jeff
>
> > org.hibernate.type.descriptor.java.JdbcDateTypeDescriptor.unwrap(JdbcDateTy­peDescriptor.java:
> > 40)
> >        at org.hibernate.type.descriptor.sql.DateTypeDescriptor
> > $1.doBind(DateTypeDescriptor.java:53)
> >        at
> > org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:
> > 91)
> >        at
>
> > org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBa­sicType.java:
> > 283)
> >        at
>
> > org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBa­sicType.java:
> > 278)
> >        at org.hibernate.loader.Loader.bindPositionalParameters(Loader.java:
> > 1873)
> >        at org.hibernate.loader.Loader.bindParameterValues(Loader.java:1844)
> >        at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:
> > 1716)
> >        at org.hibernate.loader.Loader.doQuery(Loader.java:801)
> >        at
>
> > org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.j­ava:
> ...
>
> read more »- Hide quoted text -

Jeff Johnston

unread,
Feb 6, 2012, 1:55:26 PM2/6/12
to jmesa...@googlegroups.com
You would have to look it up in Hibernates docs. Its been awhile since I have used Hibernate (I like using OpenORM nowadays). I would not neccessarily use my examples as the only way to use the API. I mean I think its fine to do it like that, but really its just the way I chose to do it when I made my example up.

What I was saying before is the extent that I can help you with is getting the values from the Limit, and it sounds like that is working fine. How you integrate it from there is up to you :).

-Jeff

Sam Welter

unread,
Feb 7, 2012, 11:42:47 AM2/7/12
to jmesa_forum
Gotcha! :-)

Since you've been so helpful, I hope you will answer one more
question. I'd like to pop a javascript calendar when a user clicks on
the date filter fields. Any suggestions on where I might start with
this?

Thanks again for everything!

Sam

On Feb 6, 12:55 pm, Jeff Johnston <jeff.johnston...@gmail.com> wrote:
> You would have to look it up in Hibernates docs. Its been awhile since I
> have used Hibernate (I like using OpenORM nowadays). I would not
> neccessarily use my examples as the only way to use the API. I mean I think
> its fine to do it like that, but really its just the way I chose to do it
> when I made my example up.
>
> What I was saying before is the extent that I can help you with is getting
> the values from the Limit, and it sounds like that is working fine. How you
> integrate it from there is up to you :).
>
> -Jeff
>

Jeff Johnston

unread,
Feb 8, 2012, 10:36:32 AM2/8/12
to jmesa...@googlegroups.com
No problem...what you need to do is create you own FilterEditor. Take a look at the HtmlFilterEditor and/or the DroplistFilterEditor as examples. Then also look at the tutorial to show you how to plug one in.

http://code.google.com/p/jmesa/wiki/CustomDroplistFilterEditorTutorial

The really custom part for you is that you will need to modify the jquery.jmesa.js to work with whatever calendar you want to use. Unfortunately there is not a way to plug the JavaScript in so you will need to modify the file directly and carry a custom version (for now).

Basically just about every piece of JMesa functionality can easily be swapped out for something custom :).

-Jeff
Reply all
Reply to author
Forward
0 new messages