Lazy loading with JPA criterea - primefaces

I implemented a generic solution for using lazy loading primefaces datatables using JPA Criterea.
However I am still having some doubts with thie implemented solution whenever we deal with several Joins (say for example an entity User that has relation with other entities like Account, Address, Department.....in addition to raw type properties like: String username, Date birthdate...etc).
I tested this solution but I am having some delays while loading huge number of data (however the solution is supposed to load only a limited number of rows specified by PageSize coming from datatable), so:
How to improve the performance of this solution?
How to be sure the number of loaded data is the one specified in the Pagesize?
Can you check the count() method and tell if it counts the number of result rows without loading all the data?
And most importantly how to use this solution in order to be generic with filters coming from Search forms (I mean how to use this sae generic method and give search critereas from a search form with multi search fields)?
Please I need your answer on the above mentioned questions especially the last one.
Here is the code:
public <T extends Object> List<T> search(Class<T> type, int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, String> filters){
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<T> q = cb.createQuery(type);
Root<T> root=q.from(type);
q.select(root);
//Sorting
if (sortField != null && !sortField.isEmpty()) {
String[] sortingField = sortField.split("\\.", 2);
Path path = sortingField.length == 1 ? root.get(sortingField[0]): root.join(sortingField[0]).get(sortingField[1]);
if (sortOrder.equals(SortOrder.ASCENDING)) {
q.orderBy(cb.asc(path));
} else if (sortOrder.equals(SortOrder.DESCENDING)) {
q.orderBy(cb.desc(path));
}
}
// Filtering
Predicate filterCondition = cb.conjunction();
String wildCard = "%";
for (Map.Entry<String, String> filter : filters.entrySet()) {
String[] filterField = filter.getKey().split("\\.", 2);
Path path = filterField.length == 1 ? root.get(filterField[0]): root.join(filterField[0]).get(filterField[1]);
filterCondition = cb.and(filterCondition, filter.getValue().matches("[0-9]+")
? cb.equal(path, Long.valueOf(filter.getValue()))
: cb.like(path, wildCard + filter.getValue() + wildCard));
}q.where(filterCondition);
//Pagination
TypedQuery<T> s = entityManager.createQuery(q);
if (pageSize >= 0){
s.setMaxResults(pageSize);
}
if (first >= 0){
s.setFirstResult(first);
}
log.info("\n\n\n");
log.info("XXXXXXXXXxX");
log.info("=> CommonRepository - Total number of rows returned: ");
log.info("XXXXXXXXXXX");
log.info("\n\n\n");
return s.getResultList();
}
public <T extends Object> int count(Class<T> type, Map<String, String> filters){
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = cb.createQuery(Long.class);
Root<T> root=cq.from(type);
// Filtering
Predicate filterCondition = cb.conjunction();
String wildCard = "%";
for (Map.Entry<String, String> filter : filters.entrySet()) {
String[] filterField = filter.getKey().split("\\.", 2);
Path path = filterField.length == 1 ? root.get(filterField[0]): root.join(filterField[0]).get(filterField[1]);
filterCondition = cb.and(filterCondition, filter.getValue().matches("[0-9]+")
? cb.equal(path, Long.valueOf(filter.getValue()))
: cb.like(path, wildCard + filter.getValue() + wildCard));
}cq.where(filterCondition);
cq.select(cb.count(root));
return entityManager.createQuery(cq).getSingleResult().intValue();
}

Related

Disable sorting fields value alphabetically

I am using Spring Boot (v2.1.3 RELEASE) and SpringDoc. I already went through https://springdoc.org/springdoc-properties.html and https://springdoc.org/, but it looks like SpringDoc is automatically sorting the parameters alphabetically. How can we prevent this?
#Operation(summary = "Find Students")
#Parameter(in=ParameterIn.QUERY, name="page", description="Results page you want to retrieve (0..N)", schema=#Schema(defaultValue = 0))
#Parameter(in=ParameterIn.QUERY, name="size", description="Number of records per page.", schema=#Schema(defaultValue =50))
#Parameter(in=ParameterIn.QUERY, name="search_type", description=AppConstants.SEARCH_TYPE, schema=#Schema(allowableValues= {"Starts", "Contains"},defaultValue = "Starts"))
#ApiCountryHeader
#GetMapping(value = "/students")
public ResponseEntity<List<Students>> findStudentss(
#Parameter(description = "") #RequestParam(required = false) String studentCd,
#Parameter(description = "") #RequestParam(required = false) String studentName,
#Parameter(hidden=true) String search_type){
....
....
...
return new ResponseEntity<>(studentts, HttpStatus.OK);
}
Fields are not sorted alphabetically, but the order of declaration is preserved.
You can change the fields order, using the different available customizers:
OpenApiCustomiser: To customize the OpenAPI object
OperationCustomizer: To customize an operation based on the HandlerMethod
For example:
#RestController
public class HelloController {
#GetMapping(value = "/persons")
public String getPerson(String b, String a) {
return null;
}
#Bean
OperationCustomizer operationCustomizer() {
return (Operation operation, HandlerMethod handlerMethod) -> {
if ("getPerson".equals(handlerMethod.getMethod().getName())) {
List<Parameter> parameterList = operation.getParameters();
if (!CollectionUtils.isEmpty(parameterList))
Collections.reverse(parameterList);
}
return operation;
};
}
}

Displaying data from SQLite table's columns, one of which holds an array

I managed to retrieve the SQLite table with only the first item of the array and put it in the UI's TextView. Couldn't get all the of the array's items. From each of the rest of the columns, a single value is returned successfully.
The JSON is parsed and passed as a parcelable ArrayList to a Fragment where it's presented in a list. Clicking on a list item directs to another Fragment where all the of item's details are presented.
I've been trying to write a for loop that returns the Strings in the array into the TextView, but the condition i < genresList.size() is always false. I tried using a while loop, but it returns only the first item of the list.
Various ways I've found on the internet didn't work.
Thanks.
Parsing and insertion to SQLite
private void parseJsonAndInsertToSQLIte(SQLiteDatabase db) throws JSONException {
// parsing the json
String jsonString = getJsonFileData();
JSONArray moviesArray = new JSONArray(jsonString);
ContentValues insertValues;
for (int i = 0; i < moviesArray.length(); i++) {
JSONObject jsonObject = moviesArray.getJSONObject(i);
String title = jsonObject.getString("title");
String imageUrl = jsonObject.getString("image");
String rating = jsonObject.getString("rating");
String releaseYear = jsonObject.getString("releaseYear");
JSONArray genresArray = jsonObject.getJSONArray("genre");
List<String> genres = new ArrayList<>();
for (int k = 0; k < genresArray.length(); k++) {
genres.add(genresArray.getString(k));
}
insertValues = new ContentValues();
insertValues.put(Movie.TITLE, title);
insertValues.put(Movie.IMAGE_URL, imageUrl);
insertValues.put(Movie.RATING, rating);
insertValues.put(Movie.RELEASE_YEAR, releaseYear);
for (int k = 0; k < genresArray.length(); k++) {
insertValues.put(Movie.GENRE, genres.get(k));
}
Log.i(TAG, "insertValues: " + genresArray);
long res = db.insert(TABLE_NAME, null, insertValues);
Log.i(TAG, "parsed and inserted to sql - row: " + res);
}
}
The item's details Fragment
public class MovieDetailsFragment extends Fragment{
... variables declarations come here...
#Nullable
#Override
public View onCreateView(#NotNull LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_details_movie, container, false);
Context context = getActivity();
Bundle idBundle = getArguments();
if (idBundle != null) {
movieId = getArguments().getInt("id");
}
getDatabase = new GetDatabase(context);
getDatabase.open();
Cursor cursor = getDatabase.getMovieDetails(movieId);
... more irelevant code comes here...
titleView = rootView.findViewById(R.id.movieTtlId);
ratingView = rootView.findViewById(R.id.ratingId);
releaseYearView = rootView.findViewById(R.id.releaseYearId);
genreView = rootView.findViewById(R.id.genreID);
String titleFromSQLite = cursor.getString(cursor.getColumnIndex(Movie.TITLE));
String ratingFromSQLite = cursor.getString(cursor.getColumnIndex(Movie.RATING));
String releaseYearFromSQLite = cursor.getString(cursor.getColumnIndex(Movie.RELEASE_YEAR));
String genreFromSQLite;
if(cursor.moveToFirst()) {
do {
genreFromSQLite = cursor.getString(cursor.getColumnIndex(Movie.GENRE));
genres.add(genreFromSQLite);
} while (cursor.moveToNext());
}
else{
genreFromSQLite = cursor.getString(cursor.getColumnIndex(Movie.RELEASE_YEAR));
}
getDatabase.close();
//more irelevant code comes here
genreView.setText(genreFromSQLite);
genreView.setFocusable(false);
genreView.setClickable(false);
return rootView;
}
}
The method that returns the table from SQLite:
public ArrayList<Movie> getMovies() {
String[] columns = {
Movie.ID,
Movie.TITLE,
Movie.IMAGE_URL,
Movie.RATING,
Movie.RELEASE_YEAR,
Movie.GENRE
};
// sorting orders
String sortOrder =
Movie.RELEASE_YEAR + " ASC";
ArrayList<Movie> moviesList = new ArrayList<>();
Cursor cursor = db.query(TABLE_NAME, //Table to query
columns,
null,
null,
null,
null,
sortOrder);
if (cursor.moveToFirst()) {
do {
Movie movie = new Movie();
movie.setMovieId(Integer.parseInt(cursor.getString(cursor.getColumnIndex(Movie.ID))));
movie.setTitle(cursor.getString(cursor.getColumnIndex(Movie.TITLE)));
movie.setImageUrl(cursor.getString(cursor.getColumnIndex(Movie.IMAGE_URL)));
movie.setRating(cursor.getDouble(cursor.getColumnIndex(Movie.RATING)));
movie.setReleaseYear(cursor.getInt(cursor.getColumnIndex(Movie.RELEASE_YEAR)));
List<String> genreArray = new ArrayList<>();
while(cursor.moveToNext()){
String genre = cursor.getString(cursor.getColumnIndex(Movie.GENRE));
genreArray.add(genre);
}
movie.setGenre(Collections.singletonList(String.valueOf(genreArray)));
// Adding a movie to the list
moviesList.add(movie);
} while (cursor.moveToNext());
}
Log.d(TAG, "The movies list from sqlite: " + moviesList);
cursor.close();
db.close();
return moviesList;
}
I believe your issue is with :-
for (int k = 0; k < genresArray.length(); k++) {
insertValues.put(Movie.GENRE, genres.get(k));
}
That will result in just the last value in the loop being inserted as the key/column name (first parameter of the put) does not change (and probably can't as you only have the one column).
You could use :-
StringBuilder sb = new StringBuilder();
for (int k = 0; k < genresArray.length(); k++) {
if (k > 0) {
sb.append(",");
}
sb.append(genres.get(k));
}
insertValues.put(Movie.GENRE, sb.toString());
Note the above code is in-principle code. It has not been tested or run and may therefore contains errors.
This would insert all the data as a CSV into the GENRE column.
BUT that is not a very good way as far as utilising databases. It would be far better if the Genre's were a separate table and probably that a mapping table were used (but that should be another question).
This is going to cause you issues as well :-
if (cursor.moveToFirst()) {
do {
Movie movie = new Movie();
movie.setMovieId(Integer.parseInt(cursor.getString(cursor.getColumnIndex(Movie.ID))));
movie.setTitle(cursor.getString(cursor.getColumnIndex(Movie.TITLE)));
movie.setImageUrl(cursor.getString(cursor.getColumnIndex(Movie.IMAGE_URL)));
movie.setRating(cursor.getDouble(cursor.getColumnIndex(Movie.RATING)));
movie.setReleaseYear(cursor.getInt(cursor.getColumnIndex(Movie.RELEASE_YEAR)));
List<String> genreArray = new ArrayList<>();
while(cursor.moveToNext()){
String genre = cursor.getString(cursor.getColumnIndex(Movie.GENRE));
genreArray.add(genre);
}
movie.setGenre(Collections.singletonList(String.valueOf(genreArray)));
// Adding a movie to the list
moviesList.add(movie);
} while (cursor.moveToNext());
That is you move to the first row of the Cursor, extract some data MoveieId,Title ... ReleaseYear.
Then
a) if there any other rows you move to the next (which would be for a different Movie) and the next until you finally reached the last row adding elements to the genreArray.
or
b) If there is only the one row in the Cursor genreArray is empty.
You then add the 1 and only movie to the movieList and return.
1 move (row) in the Cursor will exist per movie and there is only the 1 GENRE column per movie. You have to extract the data in that column and then split the data into the genreArray without moving (see the previous fix that will create a CSV (note that would be messed up if the data contained commas)).
IF you used the previous fix and store the multiple genres as a CSV, then you could use :-
if (cursor.moveToFirst()) {
do {
Movie movie = new Movie();
movie.setMovieId(Integer.parseInt(cursor.getString(cursor.getColumnIndex(Movie.ID))));
movie.setTitle(cursor.getString(cursor.getColumnIndex(Movie.TITLE)));
movie.setImageUrl(cursor.getString(cursor.getColumnIndex(Movie.IMAGE_URL)));
movie.setRating(cursor.getDouble(cursor.getColumnIndex(Movie.RATING)));
movie.setReleaseYear(cursor.getInt(cursor.getColumnIndex(Movie.RELEASE_YEAR)));
List<String> genreArray = new List<>(Arrays.asList((cursor.getString(cursor.getColumnIndex(Movie.GENRE))).split(",",0)));
movie.setGenre(Collections.singletonList(String.valueOf(genreArray)));
// Adding a movie to the list
moviesList.add(movie);
} while (cursor.moveToNext());
Note the above code is in-principle code. It has not been tested or run and may therefore contains errors.

How to implement mysql "where ? is null " in SpringJDBCTemplate

I Want to implement a sql as follows in SpringJDBC
SELECT note from item WHERE sku = :sku AND make = :make
But the query can't find the record actually exist when make is a null property.
After debug,I found out there is a difference between " make = null " and " make is null " in MYSQL. But there is no API i can find which is suited for that situation in SpringJDBC. Any clues for that or must in static sql?
Try this:
public Item findBySkuAndMake(String sku, String make) {
Item item = null;
try {
String sql = "SELECT note FROM item WHERE sku = :sku AND make = :make";
SqlParameterSource parameters = new MapSqlParameterSource()
.addValue("sku", sku)
.addValue("make", make);
item = namedParameterJdbcTemplate.queryForObject(sql, parameters, new ItemRowMapper());
} catch (DataAccessExceptione) {
System.err.printf("Something went wrong while trying to search inside ITEM; this is why: %s%n", e);
}
// Then read getNote() ...or just return it (and change the method's return type)
return item;
}
// This is up to you, I don't know what's inside *Item*
private static class ItemRowMapper implements RowMapper<Item> {
#Override
public Item mapRow(ResultSet resultSet, int i) throws SQLException {
return new Item(resultSet.getString("note"), resultSet.getString("sku"),
resultSet.getString("make");
}
}
If you need to make one (or more) field(s) conditional, just add an if clause in the SQL query and exclude it (or them) from SqlParameterSource.

How to make the position of a LINQ Query SELECT variable

Is it possible to make the LINQ SELECT more flexible by not working with properties but with the column name?
Maybe an example will help..I'm trying to do the following (pseudocode):
From x In Entities
Where ...
Select("ID", "Value" , "Date")
but depending on certain choices, I would like to have the result in different order
From x In Entities
Where ...
Select("Value", "Date", "ID" )
Or another amount of SELECT results
From x In Entities
Where ...
Select("Value")
Any help to make this as dynamic as possible would be AWESOME! tnx
Maybe this will help you
from x In Entities
where ... select new {
Value = x["Value"],
Date = x["Date"],
ID = x["ID"]
}
Like I said in my comment (handles subproperties, like Type.Name, but not multiple fields)
I let the fun to make the multi field version ;)
public static class DynamicLinkExtensions
{
public static IEnumerable<dynamic> Select<T>(this IQueryable<T> source, string memberAccess)
{
var propNames = memberAccess.Split('.');
var type = typeof(T);
var props = new List<PropertyInfo>();
foreach (var propName in propNames)
{
var prop = type.GetProperty(propName);
props.Add(prop);
type = prop.PropertyType;
}
return source.Select(props.ToArray());
}
public static IEnumerable<dynamic> Select<T>(this IQueryable<T> source, PropertyInfo[] props)
{
var parameter = Expression.Parameter(typeof(T));
var member = Expression.MakeMemberAccess(parameter, (MemberInfo)props.First());
for (var i = 1; i < props.Length; i++)
{
member = Expression.MakeMemberAccess(member, (MemberInfo)props[i]);
}
Expression<Func<T, object>> expression = Expression.Lambda<Func<T, object>>(member, new[] { parameter });
return source.Select(expression);
}
}
Usage:
var names = DataContext.Customers.Select("Name").Cast<string>().ToList();

Performing dynamic sorts on EF4 data

I'm attempting to perform dynamic sorting of data that I'm putting into grids into our MVC UI. Since MVC is abstracted from everything else via WCF, I've created a couple utility classes and extensions to help with this. The two most important things (slightly simplified) are as follows:
public static IQueryable<TModel> ApplySortOptions<TModel, TProperty>(this IQueryable<TModel> collection, IEnumerable<ISortOption<TModel, TProperty>> sortOptions) where TModel : class
{
var sortedSortOptions = (from o in sortOptions
orderby o.Priority ascending
select o).ToList();
var results = collection;
foreach (var option in sortedSortOptions)
{
var currentOption = option;
var propertyName = currentOption.Property.MemberWithoutInstance();
var isAscending = currentOption.IsAscending;
if (isAscending)
{
results = from r in results
orderby propertyName ascending
select r;
}
else
{
results = from r in results
orderby propertyName descending
select r;
}
}
return results;
}
public interface ISortOption<TModel, TProperty> where TModel : class
{
Expression<Func<TModel, TProperty>> Property { get; set; }
bool IsAscending { get; set; }
int Priority { get; set; }
}
I've not given you the implementation for MemberWithoutInstance() but just trust me in that it returns the name of the property as a string. :-)
Following is an example of how I would consume this (using a non-interesting, basic implementation of ISortOption<TModel, TProperty>):
var query = from b in CurrentContext.Businesses
select b;
var sortOptions = new List<ISortOption<Business, object>>
{
new SortOption<Business, object>
{
Property = (x => x.Name),
IsAscending = true,
Priority = 0
}
};
var results = query.ApplySortOptions(sortOptions);
As I discovered with this question, the problem is specific to my orderby propertyName ascending and orderby propertyName descending lines (everything else works great as far as I can tell). How can I do this in a dynamic/generic way that works properly?
You should really look at using Dynamic LINQ for this. In fact, you may opt to simply list the properties by name instead of using an expression, making it somewhat easier to construct.
public static IQueryable<T> ApplySortOptions<T, TModel, TProperty>(this IQueryable<T> collection, IEnumerable<ISortOption<TModel, TProperty>> sortOptions) where TModel : class
{
var results = collection;
foreach (var option in sortOptions.OrderBy( o => o.Priority ))
{
var currentOption = option;
var propertyName = currentOption.Property.MemberWithoutInstance();
var isAscending = currentOption.IsAscending;
results = results.OrderBy( string.Format( "{0}{1}", propertyName, !isAscending ? " desc" : null ) );
}
return results;
}
While I think #tvanfosson's solution will function perfectly, I'm also looking into this possibility:
/// <summary>
/// This extension method is used to help us apply ISortOptions to an IQueryable.
/// </summary>
/// <param name="collection">This is the IQueryable you wish to apply the ISortOptions to.</param>
/// <param name="sortOptions">These are the ISortOptions you wish to have applied. You must specify at least one ISortOption (otherwise, don't call this method).</param>
/// <returns>This returns an IQueryable object.</returns>
/// <remarks>This extension method should honor deferred execution on the IQueryable that is passed in.</remarks>
public static IOrderedQueryable<TModel> ApplySortOptions<TModel, TProperty>(this IQueryable<TModel> collection, IEnumerable<ISortOption<TModel, TProperty>> sortOptions) where TModel : class
{
Debug.Assert(sortOptions != null, "ApplySortOptions cannot accept a null sortOptions input.");
Debug.Assert(sortOptions.Count() > 0, "At least one sort order must be specified to ApplySortOptions' sortOptions input.");
var firstSortOption = sortOptions.OrderBy(o => o.Priority).First();
var propertyName = firstSortOption.Property.MemberWithoutInstance();
var isAscending = firstSortOption.IsAscending;
// Perform the first sort action
var results = isAscending ? collection.OrderBy(propertyName) : collection.OrderByDescending(propertyName);
// Loop through all of the rest ISortOptions
foreach (var sortOption in sortOptions.OrderBy(o => o.Priority).Skip(1))
{
// Make a copy of this or our deferred execution will bite us later.
var currentOption = sortOption;
propertyName = currentOption.Property.MemberWithoutInstance();
isAscending = currentOption.IsAscending;
// Perform the additional orderings.
results = isAscending ? results.ThenBy(propertyName) : results.ThenByDescending(propertyName);
}
return results;
}
using the code from this question's answer:
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
{
return ApplyOrder(source, property, "OrderBy");
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
{
return ApplyOrder(source, property, "OrderByDescending");
}
public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder(source, property, "ThenBy");
}
public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder(source, property, "ThenByDescending");
}
private static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
{
var props = property.Split('.');
var type = typeof (T);
var arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (var prop in props)
{
// use reflection (not ComponentModel) to mirror LINQ
var pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
var delegateType = typeof (Func<,>).MakeGenericType(typeof (T), type);
var lambda = Expression.Lambda(delegateType, expr, arg);
var result = typeof (Queryable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof (T), type)
.Invoke(null, new object[] {source, lambda});
return (IOrderedQueryable<T>) result;
}