Hibernate Search Projection - StaticAliasToBeanResultTransformer - java

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.

Related

Hibernate JPA Meta Model -- reference nested properties?

Suppose an entity model where an Employee has a Supervisor who has an id. Using hibernate-jpamodelgen to generate the meta model for the entities, how can I query a nested field?
For instance, "get all employees whose supervisor has id 4", using JpaSpecificationExecutor:
Page<Employee> getEmployeesBySupervisorId(int id) {
return findAll((root, query, criteriaBuilder) -> {
return criteriaBuilder.equal(root.get(Employee_.supervisor.id), id);
});
}
Note that Employee_ is the model meta class for Employee (and was generated by Hibernate).
This code will produce an error because the id symbol cannot be found on type SingularAttribute<Employee, Supervisor>. I get that, but it seems like these should somehow be chainable. I can't find great examples of how to do this cleanly.
In order to navigate to related entities, you must use From#join() join method, which works well with MetaModel:
CriteriaQuery<Employee> cq = criteriaBuilder.createQuery(Employee.class);
Root<Employee> from = cq.from(Employee.class);
Predicate p = criteriaBuilder.equal(from.join(Employee_.supervisor).get(Supervisor_.id), id);
See also
Oracle's Java EE Tutorial - Using the Criteria API and Metamodel API to Create Basic Typesafe Queries
Yes, I also stumbled upon this problem that the Metamodel classes are not offering deeper visibility to relationships > 1.
While accessing A.b is possible, A.b.c is not.
But there is another possibility besides Joins:
Just concatenate by using several getter(). For this you will need a root element (= CriteriaQuery & CriteriaBuilder).
return criteriaBuilder.equal(root.get(Employee_.supervisor).get(Supervisor_.id), id);
While this still ensures type safety, the whole path should be correct as it is not validated until runtime.
Also for sorting a resultset using the Metamodel there is a similar solution. Say you want to sort by the Supervisor's id:
Use JpaSort and JpaSort.Path
JpaSort.of(JpaSort.Direction.ASC, JpaSort.path(Employee_.supervisor).dot(Supervisor_.id));

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.

Filter custom fields not present in database using Apache Cayenne

In my API it is currently possible to filter on all fields that exists in the database.
Filtering is implemented in FilterUtils.java in the API project. This translates the url parameters to a Apache Cayenne search and returns the result to the resource.
In the "Main" project there are generated classes for each database table in com.foo.bar.auto these are extended by classes in com.foo.bar.
The classes in com.foo.bar can have custom functions. An example is Document.getAccount.
Document.getAccount is exposed in the API, but it is not able to filter it because it's not a database field. I need to be able to filter fields like Document.getAccount.
Is it possible to register these functions in Cayenne somehow?
The syntax for searching on custom fields needs to be equal to todays filtering syntax. So when searching for account it should look like this: Document?filter=account(EQ)1234.
Any ideas? All help is appreciated.
Your best bet is to split your filter keys into persistent and non-persistent properties. Then you'd build 2 expressions, one for each subset of keys. Use the first expression to build a query to fetch from DB, and the second one - to filter the returned result in memory:
Expression p = ...
Expression np = ...
SelectQuery query = new SelectQuery(Document.class, p);
List<Document> docs = context.performQuery(query);
List<Document> filteredDocs = np.filterObjects(p);

Hibernate criteria list iterating

I have the following section of code that I want to use to return a collection of my object:
Session session = HibernateUtil.getSession();
List<MyObj> myObjList = (List<MyObj>)
session.createCriteria(MyObj.class)
.add(Restrictions.eq("searchField", searchField)).list();
Iterator<MyObj> myObjIt = myObjList.listIterator();
log.debug("list size: " + myObjList.size());
while(myObjIt.hasNext()){
MyObj myObj = myObjIt.next();
log.debug(myObj.getMyField());
}
However, my log keeps printing the same record as many times as the size of the list. If I refactor slightly, my code works correctly like this:
SQLQuery query = session.createSQLQuery(
"select my_field from my_table where search_field = :searchField"
);
query.setParameter("myField", myField);
List result = query.list();
for(Iterator iter = result.iterator(); iter.hasNext();){
Object[] row = (Object[]) iter.next();
log.debug(row[0]);
}
Am I doing something wrong in my first code segment? I should be able to go about this either way, and since I'm using Hibernate, I'd rather the ORM be working as expected, so I'd prefer the former method over the latter. Anyone have any thoughts?
Fwiw, I am using Hibernate 3.5.4 final, Hibernate-validator 4.2.0 Final, hibernate-search 3.4.0 Final, and hibername-c3p0 3.6.5 final, all from the maven repos.
Edited to clarify based on comments.
From what you have described in the question, your both code segments should return the same results. Assuming that in first code segment Hibernate executes pretty the same query as in second segment (you can check it in log, just enable 'hibernate.show_sql' config parameter) - problem is somewhere in converting result set to MyObj list. It is pretty unlikely that it happens due to a bug in hibernate, so it can be due to incorrect entity class mapping. If you do not see any problems with mapping, please add more details to the question (your entity class with mappings, db table schema and data sample) so anyone can reproduce the issue.
Likely your MyObj class does not have Id column mapping properly defined. For example if the field/property mapped as Id has the same value for all the objects in the result list, hibernate will return same objects (as in your case).
Regarding using primitives as Id type: hibernate allow using primitive types, but it has the following line in the docs:
We recommend that you declare consistently-named identifier properties on persistent classes and that you use a nullable (i.e., non-primitive) type.
Summarizing possible Id mapping issues:
1. Id mapped column is not unique in db.
2. Corresponding setter for Id property getter mapped is not specified or does not really save passed argument value.
3. Not-nullable type of Id field.

How to add property counted in DB to #Entity class?

I have an Entity. And sometimes I need this object also contains some value, call it 'depth'. The query may look like 'select b.id, b.name, b..., count(c.id) as depth from Entity b, CEntity c where ...'. So I've created class NonHibernateEntity which extends Entity. And then results of query written above are perfectly stored as List of NonHibEntity, which consists of all the fields of Entity (as they are extended), and property 'depth'. I make it by setting aliasToBean results transformer: .setResultTransformer(Transformers.aliasToBean(NHEntity.class)).
But, it is annoying and inconvenient - to specify all the aliases of all the needed fields.
And then, if I want to save one of this object to DB - session.saveOrUpdate((Enity)nHibEntity) - there are an exception about nHibEntity isn't Hibernate Entity.
I heard about storing 'entity' as field in NonHibEntity (aggregation, not inheritance). But it seems this is rather inconvenient too.
What do you think? What is an appropriate solution?
A Formula column mapping may be suitable for your needs. I would try this first.
If this causes performance issues as you fear, you might try a mapped class hierarchy with this field only in the child, and mapping both to the same table. Not sure this will actually work though...
As a last resort, do what you've got now using a non-mapped class, but with the entity as a field in your other class - aggregation instead of inheritance as you say, and make sure there's a way of retrieving the mapped entity from the unmapped one so that you can save. It be sensible to make it a Decorator, so that it's both a subclass and aggregate and you can continue to ignore the distinction in much of your code.
With the non-mapped subclass and/or aggregate, however, you'll have to pull out the entity in order to save.
If somebody want to know - I solved this problem in such way:
just made this calculated field as #Transient, and then
List<BaseEntry> result = new ArrayList<BaseEntry>();
Iterator it = session()
.createQuery("select b, (select count(p.id) as depth from BaseEntry p " +
" where ... ) as d" +
" from BaseEntry b " +
" where ... ")
.list().iterator();
while ( it.hasNext() ) {
Object[] row = (Object[]) it.next();
BaseEntry entry = (BaseEntry) row[0];
Long d = (Long) row[1];
entry.setD(d);
result.add(entry);
}
return result;
It works good, and seems that it can be easily supported in future

Categories