Im using the CriteriaContainer Add-on in almost everything that needs a container in my application, and it works really really nice…
But I can’t at least for now, to use nested properties on a table for example. Like:
class Person {
String name;
Address address;
}
class Address {
String street;
}
CriteriaQueryDefinition<Person> cd = new CriteriaQueryDefinition<Person>(controller.getDaoEntityManager(), true, 50, Person.class);
container = new CriteriaContainer<Person>(cd);
Table t = new Table(container);
t.setVisibleColumns(new String[] {"name", "address.street"});
You can solve the problem right away if you use the BeanTupleContainer class directly (CriteriaContainer is just an extension of that class).
If you look at the JPA reference document, you will see that JPA allows you to write things like
q.multiselect(
v.get(VideoStore_.location).alias("location"),
v.get(VideoStore_.location).get(Address_.street).alias("location.address")
in the defineQuery() method (which is exactly the same as in CriteriaContainer.)
With the BeanTupleContainer, this will give you the two item properties “location” and “location.address” as you require.
I will however consider adding nested properties to CriteriaContainer.
p.s. I am currently testing a version of the container which allows you to designate a field to be returned as value instead of the container index as is currently the case. In that way, you can return the actual id from the entity. This will make life much easier when using the container with a Select or ComboBox – if you have a taskId as @Id, you will get that and will be able to use the actual foreign key directly without a lookup.
You read my mind. I will post a question exactly about the use of this container with Combos, etc. I Belive that will be more ease if value returns the object itself. Or not?
And about the support of nested properties on the CriteriaContainer. It will be great!
Caused by: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.RangeCheck(ArrayList.java:547)
at java.util.ArrayList.get(ArrayList.java:322)
at org.vaadin.addons.lazyquerycontainer.LazyQueryView.queryItem(LazyQueryView.java:237)
at org.vaadin.addons.lazyquerycontainer.LazyQueryView.getItem(LazyQueryView.java:210)
at org.vaadin.addons.beantuplecontainer.BeanTupleQueryView.getItem(BeanTupleQueryView.java:104)
at org.vaadin.addons.lazyquerycontainer.LazyQueryContainer.getItem(LazyQueryContainer.java:174)
at org.vaadin.addons.lazyquerycontainer.LazyQueryContainer.getContainerProperty(LazyQueryContainer.java:184)
at org.vaadin.addons.beantuplecontainer.BeanTupleContainer.getContainerProperty(BeanTupleContainer.java:187)
at com.vaadin.ui.AbstractSelect.getContainerProperty(AbstractSelect.java:745)
at com.vaadin.ui.Table.refreshRenderedCells(Table.java:1537)
at com.vaadin.ui.Table.attach(Table.java:2848)
at com.vaadin.ui.AbstractComponentContainer.attach(AbstractComponentContainer.java:97)
at com.vaadin.ui.AbstractComponentContainer.attach(AbstractComponentContainer.java:97)
at com.vaadin.ui.AbstractComponent.setParent(AbstractComponent.java:569)
at com.vaadin.ui.AbstractComponentContainer.addComponent(AbstractComponentContainer.java:211)
at com.vaadin.ui.TabSheet.addTab(TabSheet.java:274)
at com.vaadin.ui.TabSheet.addTab(TabSheet.java:312)
at com.vaadin.ui.TabSheet.addTab(TabSheet.java:291)
at br.com.simus.supera.ciclonev2gui.views.core.ViewManager.displayView(ViewManager.java:64)
at br.com.simus.supera.ciclonev2gui.views.main.MainMenu.notifyDisplayers(MainMenu.java:115)
at br.com.simus.supera.ciclonev2gui.views.main.MainMenu.access$1(MainMenu.java:113)
at br.com.simus.supera.ciclonev2gui.views.main.MainMenu$1.valueChange(MainMenu.java:89)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:490)
... 25 more
The code is failing in the underlying LazyContainer, and not in my wrapper, so I will have to look at the code for LazyContainer and see what might be causing the indigestion.
I will try to add a test for the nested access in the demo application.
When dealing with databases, it is easier if the value returned is the attribute value for the selected property, because the value will often a be a foreign key which can be stored and handled directly. If you have Products with a ProductCode key, when you use a Select in a Form, you likely want the productCode directly, so that you store the productCode in the field.
You would not, typically, store the product object in the field. You could do it by setting the Key to be the alias for the whole entity in BeanItem container.
Looking at your example code, I don’t get what you are trying to do with processAliases. You already get, by default, all the nested properties of all the entities you get in multiselect. If you look at the examples in the javadoc for BeanTupleItem, you will see that the “person” in the example gives you access to person.lastName
Here is the commented version of the method. Really sorry for dont do that before.
private List<Selection<?>> proccessAliases(Root<T> root) {
// Get the property names of the class by reflection
Container propertiesContainer = CriteriaContainerFilterModel.getContainerProperties(itemEntityClass);
ArrayList<Selection<?>> selections = new ArrayList<Selection<?>>();
for(Object property : propertiesContainer.getItemIds()) {
String propertyName = String.valueOf(property); // Get the name of property (ex: address, address.street)
selections.add(getPath(root, propertyName).alias(propertyName)); // Returns the final path, via recursion, like: (the path for street, will be alialised by the property address.street
}
return selections;
}
I try to use the BeanContainer without parse the aliases, but it returns only properties of Person, not the Address properties.
I dont try to JOIN it anyway, because its mapped directly on the class. The mulselect, only select the Person entity, not Person,Address.
I dont do that, because the Address is mapped inside Person. I need join them?
You DO need to select what you need. If you need Person, and Person.Address, then select these two items. The tuple will then contain the two items. JPA knows about the relationship between Person and Address, and will deal with it.
If you look back at the original post, the short code example does show how to fetch an entity and a child entity.
recently i changed all my queries to criteria queries, and I was very happy to see that vaadin has this CriteriaContainer. I’m using eclipselink, and i’m experiencing a strange behavior:
i’m using this container as a backend to a table, and when it comes to query the size, the container constructs a countquery, in which all the joins are involved. And when i have entities which foreign keys are null, they won’t be counted.
Something like:
select t0.id, t0.name from TABLE1 t0, TABLE2 t1 where (t0.table2 = t1.id);
An other thing is when i’m using a simple “select” in my querydefinition, the CriteriaItemHelper fails with a classcast exception, i’m using the CriteriaItemHelper like this now, to make it work (to accept multiselect and select as well):
For your second question, simply use multiselect instead of select. This is one area of the JPA API that is very hard to use with respect to casting. The criteria container uses tuple queries anyway, and the generated SQL is the same.
For your first question, what the container does is take the select query definition and uses it twice, with two different criteriaBuilders and criteriaQueries. In the first instance, the selection is replaced by a count. In the second, the selection is used as is.
EclipseLink should not be joining when counting. And when selecting, things aren’t any better: by insisting on joining it is behaving as if it was EAGER instead of LAZY. Furthermore, EclipseLink is broken as it appears to be completely ignoring the fact that a ManyToOne relationship is optional by default, and that it needs to perform an outer join, and not a join. Another thing I could not do with EclipseLink is a free-form cross-product reduction (i.e. select from A, B where A.id = B.id written without a join clause and without connecting annotations). I’ve also found a weird behaviour where it manages to mess up retrieval of a fixed number of items (counting from 0 instead of 1). I am not impressed.
EclipseLink seems to be very different from Hibernate and OpenJPA in rather broken ways. Perhaps there are settings I’m unaware of. I’ve found the following article to be very instructive on EclipseLink brain damage and its utter reliance on proprietary @JoinFetch extensions:
JPA Demystified
The sad end to this story is that I don’t think CriteriaContainer is doing anything wrong. The only workaround I’ve found the one time I was forced to use EclipseLink was to do ManyToMany relationships and mark them as LAZY.
I’m currently looking at my code because I need to add support for situations where the only item present is an @IdClass annotation; I will try to see if I can add support for a user-defined count query (as opposed to one generated from the select). Maybe there is plumbing inside EclipseLink that connects my two unrelated queries together, even though a cursory glance at the source would indicate otherwise.