Displaying multilevel hierarchy in Vaadin 23 grid - accordion

I need to display a list of books in the online library matching the search criteria. I do get this list from the web service and it can contain couple thousands of items. They are historical novels and sorted by the year the novel describes and here is the bean code:
#JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION, defaultImpl = Book.class)
#JsonSubTypes(#JsonSubTypes.Type(value = Series.class))
public class Book implements Serializable {
private static final long serialVersionUID = 1L;
#JsonInclude(JsonInclude.Include.NON_NULL)
private String bookId;
#JsonInclude(JsonInclude.Include.NON_NULL)
private List<Author> authors;
#JsonInclude(JsonInclude.Include.NON_NULL)
private String title;
#JsonInclude(JsonInclude.Include.NON_NULL)
private String url;
#JsonInclude(JsonInclude.Include.NON_NULL)
private Date publishedOn;
#JsonInclude(JsonInclude.Include.NON_NULL)
private String isbn;
#JsonIgnore
private Date addedOn;
#JsonInclude(JsonInclude.Include.NON_NULL)
private String addedBy;
#JsonIgnore
private Date updatedOn;
#JsonInclude(JsonInclude.Include.NON_NULL)
private String status;
#JsonInclude(JsonInclude.Include.NON_NULL)
private String annotation;
#JsonInclude(JsonInclude.Include.NON_NULL)
private Integer year;
#JsonInclude(JsonInclude.Include.NON_NULL)
private String country;
#JsonInclude(JsonInclude.Include.NON_NULL)
private byte[] highlight;
#JsonInclude(JsonInclude.Include.NON_NULL)
private String commentary;
#JsonInclude(JsonInclude.Include.NON_NULL)
private Short rating;
#JsonInclude(JsonInclude.Include.NON_NULL)
private Short seriasVolNum;
#JsonInclude(JsonInclude.Include.NON_NULL)
private List<String> tags;
#JsonInclude(JsonInclude.Include.NON_NULL)
private Book seriesId;
#JsonInclude(JsonInclude.Include.NON_NULL)
private List<ReadersReview> readersReviews;
// Setters and getters
}
Information is pretty lengthy and cannot be displayed in a simple grid. It need to be organized in several rows and some of them should be collapsible in several layers. I want it to look like following:
First I was thinking about using TreeGrid, but doesn't look it will work. From what I have read in the documentation, there should be items of the same class. Now I am thinking about using combination of grid and accordion, but not sure. Especially, because some fields shall be editable. I.e. user who left his book review, shall be able to edit it or remove it. I definitely can use a good advice. Please give it to me.

I think what #jouni is suggesting in his comment on your posting is using the Grid's Item Details (which you can find information on in the Grid's documentation under Item Details). You implement that via the Grid's setItemDetailsRenderer method. What that will do is cause a click on an item row to display whatever details you want to see for that item. If your book details must have multiple sections, you could use the "Details" component as your example image shows (or perhaps better would be a tabbed display so the user does not have to always click twice to see any details). The details will span the full width of the Grid below the item row. There can be only one details rendering per item and details will only be shown for one item row at a time, so if that's a problem, Grid Item Details won't work for you.
Alternatively, attempting to use a Details component in an item row as you have shown, probably will not work as you want, because your example image shows the details spanning columns, which is not possible in an item row. What you would have to do in that instance is have a Grid of just two columns—"Year" and "Book"—and the Book column would be a custom renderer of the entire book item: Authors, Title, Status, and Rating with each of the Details below.
Another option would be to use a Virtual List. This allows you to create arbitrary item rows. The drawback with this component is you don't have distinct columns with headers, but if your Grid only has two columns, then (as suggested in the previous paragraph), then you haven't lost much.
If you want to expand/collapse years and books, you could have a Virtual List of Details for each year, and inside each of those Details have another Virtual List of Details for each book.

Related

AssertJ: How to build custom and nested assertions

I need to compare one PatientDTO dto object with other one PatientModel model object.
Both classes are quite similar:
class PatientDTO {
private String name;
private List<AddressDTO> address;
// Constructors, getters and setters
}
class PatientModel {
private String id;
private String nameElement;
private List<AddressModel> addressElement;
// Constructors, getters and setters
}
class AddressDTO {
private String city;
private String country;
private List<String> linesElement;
// Constructors, getters and setters
}
class AddressModel {
private String city;
private String countryElement;
private List<String> linesElement;
// Constructors, getters and setters
}
Main differences are:
Some fields are not present on DTOs: PatientDTO.id doesn't exist.
Some field names contains suffixes on Model classes: PatientDTO.name <> PatientModel.nameElement.
Other issue I like to solve, is that:
Address related assertion should be shared. I mean, Address-like classes are present on other classes, for exemple, Organization, Practitioner...
I'd like to build an assertion like this:
PatientDTO patientDTO;
PatientModel patientModel;
assertThat(patientDTO).isEqual(patientModel);
Shortly:
Should I build a custom assertion?
Should I have an assertion for Address an other one for Patient containing previous Address assertion? How could I get this?
What aboud Address assertion for Patient, Organization
What I want to avoid is code like this:
assertThat(patientDTO).anySatisfy(p->{
assertThat(p.getName()).withFailMessage("expected name: "+ p.getAddress().getCity()).isEqualTo(patientModel.getNameElement());
assertThat(p.getAddress().getCity()).withFailMessage("expected city: "+ p.getAddress().getCity()).isEqualTo(patientModel.getCityElement());
assertThat(p.getAddress().getCountry()).withFailMessage("expected country: "+ p.getAddress().getCountry()).isEqualTo(patientModel.getCountryElement());
...
}
);
I want to avoid above code since Patient classes are really large. Here I've shorted them for clarity purpouses.
Any ideas?
The field-by-field recursive comparison could help for this purpose:
PatientDTO patientDTO = new PatientDTO(...);
PatientModel patientModel = new PatientModel(...);
assertThat(patientDTO).usingRecursiveComparison()
.isEqualTo(patientModel);
Some fields are not present on DTOs: PatientDTO.id doesn't exist.
There are a few methods that can be used to tune the comparison and ignore fields:
Directly with ignoringFields(String…​ fieldsToIgnore)
By regexes with ignoringFieldsMatchingRegexes(String…​ regexes)
By types with ignoringFieldsOfTypes(Class…​ typesToIgnore)
Some field names contains suffixes on Model classes: PatientDTO.name <> PatientModel.nameElement.
This is currently not supported and was also asked in https://stackoverflow.com/a/70381488/9714611. We plan to raise a feature request about it and I will update the answer once the issue link is ready.
Address related assertion should be shared. I mean, Address-like classes are present on other classes, for exemple, Organization, Practitioner...
If the target is always isEqualTo, probably a custom assertion implementation is not needed as long as the limitation of the recursive comparison about not being able to compare fields with different names is not a show-stopper. These fields would require ad-hoc comparison until a better solution is available.
If the target is to provide assertions in a domain-specific language, like:
assertThat(patientDTO).hasAddress(addressDTO);
then a custom assertion implementation can be added.
Also, there is an assertions generator with plugins for Maven and Gradle that can be used to generate assertions based on the class attributes.

Get only selected data from JPA query

I cound't find any nice solution to get only selected data from the domain?
E.g I have class:
#Entity
public class Reservation {
// private Integer RESERVATION_ID;
// private Integer id;
private long code;
private Date date;
private Client reservationClient;
private WashType reservationWashType;
private Vehicle reservationVehicle;
private Wash reservationWash;
private Worker reservationWorkerPesel;
private Review reservationReview;
private ReservationReminder reservationReminder;
}
And have simple query repository
public interface ReservationRepository extends JpaRepository<Reservation, Long> {
Reservation findByCode(long code);
}
I'd like to take from that query the Reservation object but without data's from class like Review, Worker.
So it means my result should looks like:
a whole object of Reservation which includes:
code,date,Client reservationClient,WashType reservationWashType,Vehicle reservationVehicle,Wash reservationWash, ReservationReminder reservationReminder
Is it possible to exclude it in nice way? Or if not how can I manage it?
Yes, you can easily do that so long as Review and Worker are marked to be lazily loaded.
What I mean is:
#ManyToOne(fetch = FetchType.LAZY)
private Review review;
Hibernate won't attempt to load the Review association until you call #getReview().
For situations then where you want your Reservation with the Review, you would then just need to specify at query-time that you want the relationship join-fetched.
#Query("SELECT r FROM Reservation r JOIN FETCH r.review WHERE r.code = :code")
List<Reservation> findByCode(Long code);
Remember, if Review cannot be null, make sure that #ManyToOne has the optional=false attribute so that when the join gets generated, it uses an inner join rather than an outer join to avoid performance overhead.

how to use array of string as condition in Drools decision table?

I am new to drools decision table, so my question may be invalid.
In my decision table i am using "in" in condition column.
Exampe : i have class Student and there is another class UniversityConstant.
In UniversityConstant class there is array of string subject code : public static final String[] subjectCode ={"150","920","930","940","154"};
In this case my condition not working properly(Above picture: Not working). Instead of using string array constant of java class if i use direct subject code string than it is working(Below picture:working).
In my project there are lots of string array ,so it is not possible to copy paste them in decision table excel. Even in case i use string constant in UniversityConstant class which represent all subject code like public static final String subjectCodeStr1 ="\"155\",\"920\",\"930\",\"940\",\"154\"" OR
public static final String subjectCodeStr2 ="155,920,930,940,154"; than also it is not working. My question is there any way to use string array constant or simple string which represents array of string of java
in decision table .
You can declare the constant sets as
public static final List<String> subjectCodes =
Arrays.asList( "155","920","930","940","154" );
and use
...getPrimarySub() memberOf $param
...
UniversityConstamt.subjectCodes

How to cache referenced entities in Morphia?

I'm using Morphia for MongoDB with Stripes Framework.
Let us assume I have two entities, Car (which describes a specific car, say some particular 1984 Honda Accord) and CarType (which specifies all Honda Accords of that kind):
The most natural way to model this seems:
#Entity
class Car {
#Id private String id; // VIN
private Date purchaseDate;
private Color color;
#Reference private CarType type;
// ..
}
#Entity
class CarType {
#Id private String id;
private String manufacturerId;
private float engineDisplacement;
// ..
}
This works, but is inefficient, as CarType is looked up from DB every time a Car is loaded. I would like to cache car types in memory, as they change rarely. Persistence frameworks like GORM and Hibernate would allow that out of the box, but I'm not sure how to do it under Morphia (there is a feature request raised for that).
I'd like to keep the reference to CarType, as just storing a String carTypeId would complicate the views and everything else too much.
So I thought I could do something like this:
#Entity
class Car {
#Id private String id; // VIN
private Date purchaseDate;
private Color color;
private String typeId;
#Transient private CarType type;
#Transient private CarService service = new CarServiceImpl();
public void setTypeId() {
this.typeId = typeId;
updateTypeReference();
}
#PostLoad void postLoad() {
updateTypeReference();
}
private void updateTypeReference() {
type = service.findTypeById(typeId);
}
// ..
}
class CarServiceImpl implements CarService {
#CacheResult CarType findCarTypeId(String typeId) {
datastore.get(CarType.class, typeId);
}
// ..
}
Which works and does what I want, but:
Does seem like a hack
I'd to inject the service instead using Guice, but cannot figure out how, although I have overall dependency injection working in Stripes ActionBeans.
So I'd like to either:
Learn how to inject (preferably, Guice) services into Morphia entities
or
Learn how to otherwise properly do caching for referenced entities in Morphia
or
If all else fails, switch to some other MongoDB POJO mapping approach which supports caching. But I really like Morphia so I'd rather not.
Another common approach would be to embed the CarType in each Car. That way you would only have to fetch a single entity.
Trade-offs:
You'll need an update logic for all duplicated CarTypes. Since you said that they hardly change, this should be fine performance-wise.
Duplicated data requires additional disk-space and the working set in RAM gets bigger as well.
You'll need to evaluate how this works out for your data, but data duplication to make reads faster is quite a common approach...
Since I didn't think of a better solution I am doing a #PostLoad event handler which gets the datastore class from a static variable, and can then look up the Referenced entity.
That seems like a hack and requires the datastore service to be thread-safe, but it works for me.

Spring MVC <form:options> selected value

I got relation "Many to one" between my CPVCode adn OrderType:
public class CPVCode {
#Id
#GeneratedValue
private int id;
private String cpv_code;
private String description;
#ManyToOne
#JoinColumn(name="id_parent")
private OrderType orderType;
//getters na setters: ...
}
Everything works well, but I NEED to displays selected value in my form:
<form:select path="orderType" items="${orderTypes }" itemLabel="title" itemValue="id" ></form:select>
It seems to work almost good: It displays list of all OrderTypes ( by ${orderTypes} which returns array of that objects type), it saves proper values by Hibernate, BUT thereis no way to select current value of orderType after refreshing...
your passing a list to a selectbox, so it iterates over the list. You need to change which bean the selectbox references - a single value oderType from CPVcode.
And also possibly change the selectbox to a different html form element ?