Define a method for multiple searches by hibernate in spring boot - java

My application have large search form with to many inputs for each entity attributes , I want to define a method for example criteria method for generic query search , instead of defining method for search by each entity attribute , how can I do this with hibernate or JPA ?

Using JpaSpecificationExecutor (scroll down to section 5
This way you can programmatically define what fields are you going to add to your where clause, like this .:
(Specification<Book>) (book, cq, cb) ->
cb.and(
// You can dynamically construct that array of predicates based on which fields are set in the form
cb.like(book.get("author"), "%" + author + "%"),
cb.like(book.get("title"), "%" + title + "%")
)

Related

Fetch data from mongo db in spring boot application where collection name & fields to fetch are known at runtime

I need to create a spring boot batch job in which, I need to fetch data from mongoDB where I don't have info about collection name & fields to fetch at coding time. I get this info only when the batch starts.
E.g. When batch starts, I can read properties file where I get collection name by 1 property & another property gives list of fields to fetch, third filed provides criteria/condition for query
So, due to this, I cant have a Java POJO defined which have mapping for collection or I cant create any MongoRepository\Template (collection name is known at runtime).
What I want to know is, just like plain old native SQL, if i get to know fields name & table name, on the fly, SQL can be build & can be fired to get the data:
String dynamicQuery = "SELECT " + commaSeperatedFieldsList + " FROM " + tableName + " WHERE " + criteria;
Is there any way by which same thing can be achieved in spring boot + mongo DB?
You can use MongoTemplate for this which can be autowired as spring provides and configures it for you automatically.
It has a
find(Query query, Class<T> entityClass, String collectionName)
method which lets you define a custom collection name and a custom entityClass.
For the dynamic query use BasicQuery as Query impl to pass a raw mongo json query and fields/projection as json if you want to limit the fields returned.
Use org.bson.Document as entityClass which is basically a Map implementation which lets you iterate over the fields in a dynamic manner.
mongoTemplate.find(new BasicQuery("{ name: \"mongodb\"}", "{ name: 1}"), Document.class, "your-collection-name").forEach(x -> {
x.get("name"); // access a specific field
x.forEach((key, value) -> {
// iterate over all fields
});
});
When you deal with a large result consider using MongoTemplate's stream() method in the same way as this doesn't load all documents into memory at once and you can process it during execution one by one.

Why SpEL support doesn't work in Spring Data JPA #Query?

I'm trying to avoid redundancy with passing second argument to method with list size. Instead, I use EL, but I have an error:
org.hibernate.QueryException: Not all named parameters have been set:
[$synthetic$__1] [SELECT distinct b FROM Book b join b.bookHashtags
as ht where ht.hashtagName in :tags group by b.uniqueIdentifier having
count(ht.uniqueIdentifier) = :$synthetic$__1]
#Repository
public interface BookRepository extends JpaRepository<Book, Long>, JpaSpecificationExecutor<Book> {
#Query("SELECT distinct b FROM Book b join b.bookHashtags as ht where ht.hashtagName in :tags " +
"group by b.uniqueIdentifier having count(ht.uniqueIdentifier) = :#{#tags.size()}")
List<Book> findAllBooksContainedTags(#Param("tags") Set<String> tags);
}
I use spring-data-jpa 1.11.0.RELEASE. I know that this feature was developed in 1.4 release. Why it doesn't work in my case...
The answer is simple: arbitrary expressions are not implemented/supported.
Please check carefully on Spring Data JPA documentaiton regarding Using SpEL expressions
As of Spring Data JPA release 1.4 we support the usage of restricted SpEL template expressions in manually defined queries via #Query
And the table of supported expressions contains only
Variable: entityName
Usage: select x from #{#entityName} x
Description: Inserts the entityName of the domain type associated with the given Repository. The entityName is resolved as follows: If the domain type has set the name property on the #Entity annotation then it will be used. Otherwise the simple class-name of the domain type will be used.

Hibernate not calling UserType for null-value in CriteriaUpdate

Hibernate uses to classes to represent literals:
org.hibernate.jpa.criteria.expression.LiteralExpression
org.hibernate.jpa.criteria.expression.NullLiteralExpression
The first one will put a placeholder into the query and apply UserTypes to the value, the second one will simply put null into the query, thereby ignoring any configured UserType. CriteriaUpdateImpl will choose NullLiteralExpression if passed a null as second parameter to set(Path<Y>, X) and CriteriaBuilderImpl.literal(T) refuses null-values all together.
We use a UserType which stores Calendar-objects as AES-encrypted date-strings and are forced to replace null-values by 0-length data for compatibility reasons.
So the question is whether there is any way to archive the following using only JPA-APIs with a Hibernate-backend, as this is really ugly
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaUpdate<Entity> criteriaUpdate = criteriaBuilder.createCriteriaUpdate(Entity.class);
Root<Entity> root = criteriaUpdate.from(Entity.class);
criteriaUpdate.where(criteriaBuilder.equal(root.get("id"), id));
setPathToNull((CriteriaBuilderImpl) criteriaBuilder, criteriaUpdate, root.get("value"));
Query query = entityManager.createQuery(criteriaUpdate);
LOG.info("About to perform 2. update");
LOG.info("Updated " + query.executeUpdate() + " entities");
Helper:
private <Y> CriteriaUpdate<Entity> setPathToNull(CriteriaBuilderImpl criteriaBuilder, CriteriaUpdate<Entity> criteriaUpdate, Path<Y> value) {
return criteriaUpdate.set(value, new LiteralExpression<>(criteriaBuilder, (Y)null));
}
A full working example is provided at
https://github.com/TheConstructor/NullUpdate/tree/master/src/main/java/tc/vom
You may come up with other ways of accessing the property, but the property-name is provided as String and the entity can not be loaded beforehand, as this update is part of an reading-value-error-clean-up. If the name of the column would help you, it can be used as well.

set table name with hibernate name parameters

I need to set a table name dynamically so that I use query.setText(tname,abc)
e.g: select a.name from :tname where a.id = '2'
I used setText() because when I use setString() it says "tname is a invalid parameter" because I assume that Hibernate adds '' when setting string parameters.
But even setText() does not help and gives the same exception.
How can I set the table name dynamically?
Reply to PSR:
So you mean replace table name as a java string replacement. But then we can not take support of sql injections prevention etc from hibernate right? Also How we bind parameters in hibernate in a situation where like statement,
Eg: name like "%:name%"
This also gives me Illegal argument exception: Parameter does not exist as a named parameter when i try to bind it using query.setString(name,"def");
Hibernate will not do this for you, because it works with PreparedStatements, and you can't prepare a statement where the table being queried isn't known yet.
I don't see why you would be exposing table names to end users, so preventing SQL injection doing a regular string substitution should be easy. You use some sort of business logic to determine the correct table from a list that only you know. The table name isn't coming from user input at all.
Depending on your choice of RDBMS, you may find a discriminator column, or table inheritance with partitioning to be a better way of handling a situation where identical queries are made against different tables.
It is not possible to set table name dynamically.You can set dynamically column names.it is not possible to set table name
try like this
select a.name from '+table name+'where a.id = '2'
In my opinion, There are 2 ways to resolve this issue:
1- If you are using Spring and Hibernate together, you could use SpEL and it would be like #{#entityName} as it is described here
#Entity
public class User {
#Id
#GeneratedValue
Long id;
String lastname;
}
public interface UserRepository extends JpaRepository<User,Long> {
#Query("select u from #{#entityName} u where u.lastname = ?1")
List<User> findByLastname(String lastname);
}
2-You could use CriteriaBuilder like
CriteriaQuery<YourEntity> cr = cb.createQuery(YourEntity.class);
Root<YourEntity> root = cr.from(YourEntity.class);
cr.select(root);
I copied the source codes from the provided links and they are described there much better

Hibernate Search Projection - StaticAliasToBeanResultTransformer

The Hibernate Search documentation for using a ResultTransformer gives the following example:
org.hibernate.search.FullTextQuery query =
s.createFullTextQuery( luceneQuery, Book.class );
query.setProjection( "title", "mainAuthor.name" );
query.setResultTransformer(
new StaticAliasToBeanResultTransformer(
BookView.class,
"title",
"author" )
);
List<BookView> results = (List<BookView>) query.list();
for(BookView view : results) {
log.info( "Book: " + view.getTitle() + ", " + view.getAuthor() );
}
However, the StaticAliasToBeanResultTransformer class does not exist in the Hibernate core jar.
Does anyone know if this is supposed to be a different class that I have not been able to identify yet? Or does it exist in another Hibernate project that I have not included?
I need to accomplish this idea of mapping indexed fields to properties in my "BookView" bean, since my properties and fields are not named the same. I am using Hibernate 4.1.8 and Hibernate Search 4.1.1
Right, there is no such class. See also https://forum.hibernate.org/viewtopic.php?f=9&t=1004608. Just write your own transformer by implementing org.hibernate.transform.ResultTransformer.
AFAIK there are no such class in Hibernate codebase. I believe it was part of the samples one day, but nowhere to be found since.
The easiest way is to write your own implementation.

Categories