Hibernate search , querying on associations - hibernate-search

I am new to Hibernate search , can anyone suggest me how to query on Embedded entities (one to many)
#Indexed
#Entity
public class EventDetails implements Serializable
{
#OneToMany( cascade = CascadeType.ALL )
#IndexedEmbedded
#JoinColumn( name = "event_id" )
private Set<Batches> batches;
--setter and getter--
}
and
#Entity
#Indexed
public class Batches
{
#Column( name = "batch" )
private String batch;
#ManyToOne
#ContainedIn(mappedBy="batches")
private EventDetails eventDetails;
--setter and getter--
}
Service class
public List<EventDetails> search()
{
fullTextEntityManager = org.hibernate.search.jpa.Search.getFullTextEntityManager(getEntityManager());
QueryBuilder q = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(EventDetails.class).get();
org.apache.lucene.search.Query luceneQuery = q.keyword().wildcard().onField("").matching(text).createQuery();
javax.persistence.Query jpaQuery = fullTextEntityManager.createFullTextQuery(luceneQuery, EventDetails.class);
List<EventDetails> list = jpaQuery.getResultList();
return list;
}
Now if i have to implement a full text query on "batch" property in batches table , what should i pass as a parameter to the "onField()" method in my service??
Thanks !

Please use batches.batch. You also have to index the batch field using the #Field annotation.
See also hibernate search documentation here and here
You can always use luke to see and query the fields in your index.

Related

How to append where clause to all queries that run with spring data MongoRepository?

I have entities that are persisted in MongoDB and use spring data MongoRepository to fetch data. Now i want to apply filter to all queries that executed on the entites, so i decided to use hibernate filter, something like this:
#Entity
#QueryEntity
#Document(collection = "Opportunity")
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#CompoundIndexes({
#CompoundIndex(name = "productGroup_userId_uniqueness", def = "{'productGroupCode' : 1, 'userId': 1}", unique = true)
})
#FilterDef(name = "defaultFilter",parameters = #ParamDef(name = "unitCode",type = "string"))
#Filter(name = "defaultFilter" , condition = " unitCode like :unitCode")
public class Opportunity {
#Id
#Indexed
private String id;
#Indexed
#Enumerated(EnumType.STRING)
private OpportunityStatus opportunityStatus = OpportunityStatus.OPEN;
private LeadType leadType;
#Indexed
private String userId;
#Indexed
private String productCode;
#Indexed
private String productGroupCode;
#Indexed
private Long actionId;
private String assigneeId;
#Transient
private List<AbstractCommand> commandHistory = new ArrayList<>();
#Transient
private Map<Long, Boolean> actionStatus = new HashMap<>();
private String unitCode;
}
and this is the repository class:
#Repository
public interface OpportunityRepository extends MongoRepository<Opportunity, String>, QuerydslPredicateExecutor<Opportunity> {
// this repository contains more than 20 methods
// and all of theme removed for question brevity
}
And I enabled hibernate filter on session with this way:
Session session = (entityManager).unwrap(Session.class);
session.enableFilter(filterName).setParameter("unitCode", this.getCurrentUserUnitCode());
Now, when I call OpportunityRepository.findAll(Predicate predicate, Pageable pageable) i expected to apply the defined filter on the entity, but it didn't work.
I think the reason is that MongoRepository hasn't any sense of hibernate #Filter and i should use another way to append where clause to all mongo queries that running on the Opportunity entity.

Spring Data JPA - java.sql.Timestamp is getting null for rest of the list or set of entity

Consider that i have a entity.
Class Employee {
#Id
private integer id;
private String name;
private Timestamp effectiveFrom;
}
and i have a list of value to it..
List<Employee> Employees = new ArrayList<>();
[1,"Employee1", "2019-10-10 00:00:00.000"]
[1,"Employee2", null]
[1,"Employee3", "2019-10-10 00:00:00.000"]
[1,"Employee4", "2019-10-10 00:00:00.000"]
When i do - repository.saveAll(Employees);
The first and second employee are saved correctly from third employee on wards the effectiveFrom column (Timestamp) - is getting null..
Is it expected behavior by Spring Data JPA ?
First you should not use the same id for any object. I would suggest to add the #GeneratedValue annotation for the primary key. I have create the entity with Lombok like that:
#Entity
#Data
#NoArgsConstructor
class Employee {
#Id
#GeneratedValue
private Integer id;
private String name;
private Timestamp effectiveFrom;
public Employee(String name, String effectiveFrom) {
this.name = name;
this.effectiveFrom = effectiveFrom == null ? null : Timestamp.valueOf(effectiveFrom);
}
}
At the end I create a simple jpa repository and save all entities:
#Bean
CommandLineRunner run(EmployeeRepository employeeRepository) {
return args -> {
List<Employee> employees = List.of(new Employee("Employee1", "2019-10-10 00:00:00.000"),
new Employee("Employee2", null), new Employee("Employee3", "2019-10-10 00:00:00.000"),
new Employee("Employee4", "2019-10-10 00:00:00.000"));
employeeRepository.saveAll(employees);
employeeRepository.findAll().forEach(System.out::println);
};
}
The console output looks like that:
Employee(id=1, name=Employee1, effectiveFrom=2019-10-10 00:00:00.0)
Employee(id=2, name=Employee2, effectiveFrom=null)
Employee(id=3, name=Employee3, effectiveFrom=2019-10-10 00:00:00.0)
Employee(id=4, name=Employee4, effectiveFrom=2019-10-10 00:00:00.0)
Please have a look at this short example. If you cannot find the error in your code, please post your code so I can have a look.

Table per concrete class with Hibernate OGM and mongodb

I'm using mongodb to store json documents, and since I'm using Hibernate ORM for my relational models I've decided to use the OGM for the mongo ones.
Currently all of my OGM entities share the same parent class, it looks something like:
#Entity
public abstract class Document {
private static final Gson GSON = new Gson();
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Type(type = "objectid")
protected String id;
public String id() {
return this.id;
}
#Override
public String toString() {
return Document.GSON.toJson(this);
}
}
#Entity
public class Address extends Document {
private String city;
private String street;
private int house;
}
#Entity
public class Person extends Document {
private String name;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<Address> addresses;
}
(simplified of course)
What I expected that would happen when I persist a Person instance is that two collections will be created in the db, one for Person and the other for Address, which I inferred:
The various inheritance strategies are not supported by Hibernate OGM,
only the table per concrete class strategy is used
(Supported entity mapping - Hibernate OGM documentation)
But what happens in reality is that only one collection is created with the name Document with two documents in it:
{
_id : id1,
DTYPE : Person,
name : name of person
}
{
_id : id2,
DTYPE : Address,
city : City of address,
street : Street of address
house : 3
}
What am I missing?
Thanks
I think, it should be:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Document {
...
}

JPA Query Many To One nullable relationship

I have the following entities and would like to seek help on how to query for selected attributes from both side of the relationship. Here is my model. Assume all tables are properly created in the db. JPA provider I am using is Hibernate.
#Entity
public class Book{
#Id
private long id;
#Column(nullable = false)
private String ISBNCode;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY, optional = false)
private Person<Author> author;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY, optional = true)
private Person<Borrower> borrower;
}
#Inheritance
#DiscriminatorColumn(name = "personType")
public abstract class Person<T>{
#Id
private long id;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Info information;
}
#Entity
#DiscriminatorValue(PersonType.Author)
public class Author extends Person<Author> {
private long copiesSold;
}
#Entity
#DiscriminatorValue(PersonType.Borrower)
public class Borrower extends Person<Borrower> {
.....
}
#Entity
public class Info {
#Id
private long id;
#Column(nullable=false)
private String firstName;
#Column(nullable=false)
private String lastName;
......;
}
As you can see, the book table has a many to one relation to Person that is not nullable and Person that is nullable.
I have a requirement to show, the following in a tabular format -
ISBNCode - First Name - Last Name - Person Type
How can I write a JPA query that will allow me to select only attributes that I would want. I would want to get the attributes ISBN Code from Book, and then first and last names from the Info object that is related to Person Object that in turn is related to the Book object. I would not want to get all information from Info object, interested only selected information e.g first and last name in this case.
Please note that the relation between the Borrower and Book is marked with optional=true, meaning there may be a book that may not have been yet borrowed by someone (obviously it has an author).
Example to search for books by the author "Marc":
Criteria JPA Standard
CriteriaQuery<Book> criteria = builder.createQuery( Book.class );
Root<Book> personRoot = criteria.from( Book.class );
Predicate predicate = builder.conjunction();
List<Expression<Boolean>> expressions = predicate.getExpressions();
Path<Object> firtsName = personRoot.get("author").get("information").get("firstName");
expressions.add(builder.equal(firtsName, "Marc"));
criteria.where( predicate );
criteria.select(personRoot);
List<Book> books = em.createQuery( criteria ).getResultList();
Criteria JPA Hibernate
List<Book> books = (List<Book>)sess.createCriteria(Book.class).add( Restrictions.eq("author.information.firstName", "Marc") ).list();
We recommend using hibernate criterias for convenience and possibilities.
Regards,

JPA Criteria API predicates for objects in OneToMany relationship

Given the following code
#Entity
public class Invoice {
#GeneratedValue(strategy = GenerationType.AUTO)
#Id
public Long id;
#Embedded
private InvoiceData data = new InvoiceData();
}
#Embeddable
public class InvoiceData {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
public Collection<InvoiceLineItem> lineItems;
}
#Entity
public abstract class InvoiceLineItem {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column
private String description;
}
#Entity
public class GoodsLineItem extends InvoiceLineItem {
}
#Entity
public class CostLineItem extends InvoiceLineItem {
}
How would I write a criteria api query that returns all Invoices with a CostLinesItem that's description is 'TAX'?
I am using the metadata API. I have tried various approaches most of which are variations of the 2 listed below. Any pointers/help or 'go read this's will be greatly appreciated.
Attempt 1 (of many):
#Test
public void criteria_api_and_collections() throws Exception {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Invoice> query = builder.createQuery(Invoice.class);
Root<Invoice> root = query.from(Invoice.class);
Join<InvoiceData, InvoiceLineItem> lineItems = root.join(Invoice_.data).join(InvoiceData_.lineItems);
query.where(builder.equal(lineItems.get(InvoiceLineItem_.description), ""));
List<Invoice> resultList = em.createQuery(query).getResultList();
System.out.println(resultList);
}
Attempt 2 (of a many):
#Test
public void criteria_api_and_collections() throws Exception {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Invoice> query = builder.createQuery(Invoice.class);
Root<Invoice> root = query.from(Invoice.class);
Join<InvoiceData, InvoiceLineItem> lineItems = root.join(Invoice_.data).join(InvoiceData_.lineItems, JoinType.LEFT);
Subquery<CostLineItem> subquery = query.subquery(CostLineItem.class);
Root<CostLineItem> fromLineItem = subquery.from(CostLineItem.class);
subquery.select(fromLineItem);
subquery.where(builder.equal(lineItems.get(InvoiceLineItem_.description), "TAX"));
query.where(builder.in(lineItems).value(subquery));
List<Invoice> resultList = em.createQuery(query).getResultList();
}
Both attempts causes a SQL grammer Exception. An alias is referred to in the resulting SQL that is never created. It looks like the alias should have been assigned to a join in the SQL that does not exist. In other words the InvoiceLineItems are not fetched in the query.
I am not able to make a test right now, but sticking to the Java EE 6 Tutorial, we see that
Embeddable classes may also contain relationships to other entities or
collections of entities. If the embeddable class has such a
relationship, the relationship is from the target entity or collection
of entities to the entity that owns the embeddable class.
This makes me think that the Join Predicate should be defined with the starting Entity Invoice instead of InvoiceData. And this is supported also by the fact that normally the starting Entity should be the query root itself. I would try with something like this:
Join<Invoice, InvoiceLineItem> lineItems = root.join(Invoice_.data).join(InvoiceData_.lineItems);
I swapped out Hibernate 4.1.0.Final for EclipseLink 2.0.0 and it worked.