How can i get entity object with contion JPA - jpa

I have 2 Objects Site and Page.
A Site have many Pages.
I get pages with fetch Lazy type.
With a particular reason i want to get pages of a site where the date of pages > val.
#Entity
#Table(name = "site")
Public class Site {
String site_id;
Set<Page> pages;
#OneToMany(mappedBy = "site", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Set<Page> getPages() {
return pages;
}
}
#Entity
#Table(name = "page")
Public class Page{
String page_id;
Site site;
#ManyToOne(fetch=FetchType.LAZY, cascade = CascadeType.MERGE)
#JoinColumn(name = "site_id")
public Site getSite() {
return site;
}
}
Now in SiteDao i have the mothode to call a site and its pages
#Stateless
public class SiteDao {
#PersistenceContext(unitName = "name")
private EntityManager em;
public Site getSiteAndPages(String site_id) {
Query q = em.createQuery("select s From Site s where s.site_id = :site_id");
q.setParameter("site_id", site_id);
Site s = (Site) q.getSingleResult();
s.getPages();
return s;
}
}
This turns well, but s.getPages() return all the pages and i want to get only some pages using where conditions.
I tried many options like:
Query q = em.createQuery(
"select s, p From Site s"
+ " join s.pages p "
+ " where s.site_id = :site_id "
+ " and p.site = s"
+ " and p.lastupdate > :val"
);
q.setParameter("site_id", site_id);
q.setParameter("val", lastUpdate);
Im bloked after many searches, does any person have an idea about how can i fix this ?
B.R

You need some business methods, you can keep inside Site Class
There are two options
1. Add filter
getLatestPages(Date lastupdate)
{
List pages_= s.getPages();
List latestPages=new ArrayList();
for(Page p: pages_)
{
if(p.getLastUpdatedate().after(lastupdate)) {
latestPages.add(p);
}
}
reutrn latestPages;
}
2.Use query
getLatestPages(EntityManager em, Date lastupdate)
{
Query q = em.createQuery(
"select p From Site s"
+ " join s.pages p "
+ " where s.site_id = :site_id "
+ " and p.site = s"
+ " and p.lastupdate > :val"
);
q.setParameter("site_id", this.site_id);
q.setParameter("val", lastUpdate);
return q.getResultList();
}
Hope this work.

Related

JPA Native Query across multiple tables

I have the following defined as a native query in a repository (dispenseRepository) :
#Query(
value = "SELECT p.*, c.*, s.*, d.* from patient p, consult c ,script s,dispense d "
+ " where p.patient_id=c.patient_id "
+ " and c.consult_id = d.consult_id "
+ " and c.fk_script_id =s.script_id"
+ " and c.consult_id=?1 ",
nativeQuery = true
)
List<Dispense> findInvoiceByConsultId(Long consultId);
The Rest Controller has :
#RequestMapping(value = "/api/invoice/{consultId}",method = {RequestMethod.GET})
public List<Dispense> invoice(#PathVariable(value="consultId")Long consultId){
return dispenseRepository.findInvoiceByConsultId(consultId);
}
When I hit the api I only get dispense details:
[
{
"dispenseId": 1,
"icd10": "J.10",
"tariffCode": "10010",
"dispenseItem": "Lenses",
"price": 400.0
},
{
"dispenseId": 3,
"icd10": "J.10",
"tariffCode": "111000",
"dispenseItem": "Other",
"price": 1500.0
},
{
"dispenseId": 4,
"icd10": "K.100",
"tariffCode": "10010",
"dispenseItem": "Eye Test",
"price": 550.0
}
]
I'd like all the data as per query which will be used for Jasper report
patient-consult 1-M
consult-script 1-1
consult-dispense 1-M
Since in your query you return all fields from all tables: SELECT p.*, c.*, s.*, d.* from patient p, consult c ,script s,dispense d creating projections/DTOs for so many objects and fields is very cumbersome. There are 2 ways to proceed. Either specify exactly the fields you want from each table in your query and create a DTO to hold those fields.
e.g.
Approach 1:
I chose only one field from each table to make it as example. Please not that you have to convert your query from native to jpa one!
#Query("SELECT new com.example.demo.ResultDTO(p.patientName, c.reservationNumber, s.addition, d.dispenseItem) from Patient p, Consult c, Script s, Dispense d ...")
List<ResultDTO> findInvoiceByConsultId(Long consultId);
and ResultDTO class can be:
package com.example.demo;
public class ResultDTO {
private String patientName;
private String reservationNumber;
private String addition;
private String dispenseItem;
public ResultDTO(String patientName, String reservationNumber, String addition, String dispenseItem) {
this.patientName = patientName;
this.reservationNumber = reservationNumber;
this.addition = addition;
this.dispenseItem = dispenseItem;
}
public String getPatientName() {
return patientName;
}
public void setPatientName(String patientName) {
this.patientName = patientName;
}
public String getReservationNumber() {
return reservationNumber;
}
public void setReservationNumber(String reservationNumber) {
this.reservationNumber = reservationNumber;
}
public String getAddition() {
return addition;
}
public void setAddition(String addition) {
this.addition = addition;
}
public String getDispenseItem() {
return dispenseItem;
}
public void setDispenseItem(String dispenseItem) {
this.dispenseItem = dispenseItem;
}
}
UPDATE
Approach 1 won't work with a nativeQuery, you have to convert it to jpa one so unless you convert your query to jpql, the above code wont work.
OR the much easier but bulkier, keep the query as is and place the result on a List of Maps.
Approach 2:
#Query(
value = "SELECT p.*, c.*, s.*, d.* from patient p, consult c ,script s,dispense d "
+ " where p.patient_id=c.patient_id "
+ " and c.consult_id = d.consult_id "
+ " and c.fk_script_id =s.script_id"
+ " and c.consult_id=?1 ",
nativeQuery = true
)
List<Map<String, Object>> findInvoiceByConsultId(Long consultId);

Search internal substrings in hibernate search

I've defined my entity as following.
#Entity
#Indexed
#AnalyzerDef(name = "ngram_index", tokenizer = #TokenizerDef(factory = WhitespaceTokenizerFactory.class),
filters = {
#TokenFilterDef(factory = LowerCaseFilterFactory.class),
#TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
#TokenFilterDef(factory = NGramFilterFactory.class,
params = {
#Parameter(name = SearchConstants.MIN_GRAM_SIZE_NAME, value = SearchConstants.MIN_GRAM_SIZE_VALUE),
#Parameter(name = SearchConstants.MAX_GRAM_SIZE_NAME, value = SearchConstants.MAX_GRAM_SIZE_VALUE)
})
})
#AnalyzerDef(name = "ngram_query", tokenizer = #TokenizerDef(factory = WhitespaceTokenizerFactory.class),
filters = {
#TokenFilterDef(factory = LowerCaseFilterFactory.class),
#TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
})
#NormalizerDef(name = "lowercase",
filters = {
#TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
#TokenFilterDef(factory = LowerCaseFilterFactory.class)
}
)
#Table(name = "ORDER")
public class Order {
#Id
#DocumentId
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Field(analyzer = #Analyzer(definition = "ngram_index"))
#Field(name = "name_Sort", store = Store.YES, normalizer= #Normalizer(definition="lowercase"))
#SortableField(forField = "name_Sort")
#Column(name = "NAME")
private String name;
//other fields, getters and setters omitted for brevity
I then tried to overwrite the default analyzer that is being used during indexing for querying in another class that is not an entity.
public abstract class AbstractHibernateSearcher<S extends SearchableEntity> {
// other fields and methods omitted here
protected Query buildInputSearchQuery(String[] searchableFields) {
if(Strings.isNullOrEmpty(searchRequest.getQuery()) || searchableFields.length == 0) {
return null;
}
SimpleQueryStringMatchingContext simpleQueryStringMatchingContext = queryBuilder.simpleQueryString().onField(searchableFields[0]);
for(int i = 1; i < searchableFields.length; i++) {
simpleQueryStringMatchingContext = simpleQueryStringMatchingContext.andField(searchableFields[i]);
}
Query inputSearchQuery = simpleQueryStringMatchingContext
.withAndAsDefaultOperator()
.matching((searchRequest.getQuery()).toLowerCase()).createQuery();
QueryBuilder queryBuilder = getNGramQueryBuilder(searchableFields);
return queryBuilder.bool().must(inputSearchQuery).createQuery();
}
protected QueryBuilder getNGramQueryBuilder(String[] searchFields) {
if (searchFields.length == 0) {
return null;
}
EntityContext entityContext = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(clazz);
for(String field : searchFields) {
entityContext = entityContext.overridesForField(field, "ngram_query");
}
return entityContext.get();
}
}
This gives me the following error when I do a query search.
{message: "HSEARCH000353: Unknown analyzer: 'ngram_query'. Make sure you defined this analyzer.",…}
exception: "RuntimeException"
message: "HSEARCH000353: Unknown analyzer: 'ngram_query'. Make sure you defined this analyzer."
I found this from the official document.
You can use #AnalyzerDef on any:
#Indexed entity regardless of where the analyzer is applied to;
parent class of an #Indexed entity;
package-info.java of a package containing an #Indexed entity.
Since I am seeing the unknown analyzer, I guess the class where I tried to overwrite with "ngram_query" analyzer has no visiblity on this analyzer?
Yes, you can create ngrams for each word: use a WhitespaceTokenizerFactory for your tokenizer, and add NGramFilterFactory to your token filters (note it's not the same class you mentioned: it's a token filter, not a tokenizer).
You will also need to use a different analyzer at query time, one that does not create ngrams. Otherwise a user typing "manhantan" may get a match for documents containing "man", for example.
See https://stackoverflow.com/a/56107399/6692043 for information on how to do that.
Note that ngrams can lead to very large indexes, especially if you're not careful with the value of the "minGramSize" and "maxGramSize" parameters.
Another solution would be to use your original analyzer and a wildcard query, but unfortunately it ignores analysis and can be quite slow when using leading wildcards (which is what you need here).
protected Query inputFilterBuilder() {
String[] searchableFields = getSearchableFields();
if(searchableFields.length == 0) {
return null;
}
TermMatchingContext termMatchingContext = queryBuilder.keyword().wildcard().onField(searchableFields[0]);
for(int i = 1; i < searchableFields.length; i++) {
termMatchingContext = termMatchingContext.andField(searchableFields[i]);
}
return termMatchingContext
.matching(("*" + searchRequest.getQuery() + "*").toLowerCase()).createQuery();
}
Note the code above will only work if there is a single search term. As soon as there are spaces in searchRequest.getQuery(), you won't get any result. There can be spaces in the indexed text, however, which is what you wanted, if I understood correctly.

returning a value using REST

I am getting errors when I am trying to return values using REST. The error is:
A HTTP GET method, public - should not consume any entity.
This is my class:
public class StockManagement {
ArrayList<String> items = new ArrayList<>();
ArrayList<Integer> stockLevel = new ArrayList<>();
#GET
#Produces("application/xml")
public String addItem(String item) {
if(items.contains(item)) { // returns true is item is exists else false
String r = "Item is already in list";
String result = "#Produces(\"application/xml\")" + r;
return "<StockManagementService>"+ "<div>" + result + "</div>" +"</StockManagementService>";
}
else {
String r = "Item has been added successfully";
String result = "#Produces(\"application/xml\")" + r;
items.add(item); // add item to inventory
stockLevel.add(0); // set the number of stock for the item in inventory
return "<StockManagementService>" +"<div>" + result + "</div>" +"</StockManagementService>";
}
}
#GET
#Produces("application/xml")
public String setStock(String item, int stockLevels) {
if(!items.contains(item)) {
String r = "Item is not in the inventory";
String result = "#Produces(\"application/xml\")" + r;
return "<StockManagementService>" + result + "</StockManagementService>";
}
else {
int index = items.indexOf(item);
stockLevel.set(index, stockLevels);
String r = "Set stock has been complete successfully";
String result = "#Produces(\"application/xml\")" + r;
return "<StockManagementService>" + result + "</StockManagementService>";
}
}
#GET
#Produces("application/xml")
public String addStock(String item, int numItem) {
if(!items.contains(item)) {
String r = "Error, Cannot add item";
String result = "#Produces(\"application/xml\")" + r;
return "<StockManagementService>" + result + "</StockManagementService>";
}
else {
int index = items.indexOf(item);
String r = "Successfully added stock";
String result = "#Produces(\"application/xml\")" + r;
return "<StockManagementService>" + result + "</StockManagementService>";
}
}
#GET
#Produces("application/xml")
public String removeStock(String item, int numItem) {
if(items.contains(item)) {
int index = items.indexOf(item);
int val = stockLevel.get(index);
val = val - numItem;
stockLevel.set(index, val);
String r = "Successfully removed item.";
String result = "#Produces(\"application/xml\")" + r;
return "<StockManagementService>" + result + "</StockManagementService>";
}
else {
String r = "Item is not in the inventory";
String result = "#Produces(\"application/xml\")" + r;
return "<StockManagementService>" + result + "</StockManagementService>";
}
}
This is the error shown on eclipse terminal:
SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
WARNING: A HTTP GET method, public java.lang.String com.crunchify.restjersey.StockManagement.setStock(java.lang.String,int), should not consume any entity.
WARNING: A HTTP GET method, public java.lang.String com.crunchify.restjersey.StockManagement.addStock(java.lang.String,int), should not consume any entity.
WARNING: A HTTP GET method, public java.lang.String com.crunchify.restjersey.StockManagement.removeStock(java.lang.String,int), should not consume any entity.
WARNING: A HTTP GET method, public java.lang.String com.crunchify.restjersey.StockManagement.addItem(java.lang.String), should not consume any entity.
SEVERE: Consuming media type conflict. The resource methods public java.lang.String com.crunchify.restjersey.StockManagement.addStock(java.lang.String,int) and public java.lang.String com.crunchify.restjersey.StockManagement.setStock(java.lang.String,int) can consume the same media type
SEVERE: Consuming media type conflict. The resource methods public java.lang.String com.crunchify.restjersey.StockManagement.removeStock(java.lang.String,int) and public java.lang.String com.crunchify.restjersey.StockManagement.setStock(java.lang.String,int) can consume the same media type
SEVERE: Consuming media type conflict. The resource methods public java.lang.String com.crunchify.restjersey.StockManagement.addItem(java.lang.String) and public java.lang.String com.crunchify.restjersey.StockManagement.setStock(java.lang.String,int) can consume the same media type
I cannot figure out what this error means, obviously it has to be the way I am returning, any help would be appreciated.
Thanks.
No promises, but I think the WARNING is trying to remind you that, in HTTP, GET doesn't take a message body. So String item should probably be encoded into the URI itself, which might mean a #QueryParam or #PathParam annotation.
SEVERE is trying to tell you that there are multiple methods that are all trying to be mapped to the same route. That is to say, they are all mapped to the same URI with the same method and the same application type, so how is the routing logic supposed to choose between them.
That might mean that you need to specify different paths for each, or that you should have only one annotated method that has the logic to choose which implementation to use.

Inheritance doesn't work in Query

I'm new to Spring and i have an issue with the following query:
String rejected_offer_query = "SELECT b.job_instance_id, b.start_time, count (*) " +
"FROM SygaOfferRejected s , BatchJobExecution b, BatchJobInstance bi "+
"where s.heure_debut = b.start_time " +
"and s.heure_fin = b.end_time " +
"and b.job_execution_id = bi.job_instance_id " +
"and bi.job_name = :batchName "+
"and b.status = :batchStatus " +
"group by b.job_instance_id";
It doesn't return data, but it should return one row as a result: (Test made directly with MySql).
I think it's an inheritance problem because when i try to execute the same query with a super class it works :
SELECT b.job_instance_id, b.start_time, count (*) " +
"FROM SygaOffer s , BatchJobExecution b, BatchJobInstance bi "+
"where s.heure_debut = b.start_time " +
"and s.heure_fin = b.end_time " +
"and b.job_execution_id = bi.job_instance_id " +
"and bi.job_name = :batchName "+
"and b.status = :batchStatus " +
"group by b.job_instance_id"
The SygaOfferRejected class extends the SygaOffer, here's the source code;
#Entity
#Inheritance
#Table(name = "bob_syga_off")
public class SygaOffer {
#Id
private long id_offre;
private String acteur;
private String heure_debut;
private String heure_fin;
private String reference_offre;
private int retry;
}
------------------------------------------
#Entity
#Table(name ="bob_syga_offr_rejected")
public class SygaOfferRejected extends SygaOffer{
}
The inheritance strategy is the issue i should specify the TABLE_PER_CLASS type in the parent class :
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
Hope that helps ;)

RestEasy - Unable to find MessageBodyReader ... application/xml?

i try since 2 days to find something about this problem, still don't get it. I got my Maven-Project running on Wildfly.
Rest-Code:
#Override
#GET
#Path("{id}")
// #Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Customer getOverId(#PathParam("id") String id) {
logger.info("put in " + this.getClass().getName() + " over id " + id);
// if (id != null) {
// Customer object = service.loadOneCustomer(new Integer(id));
// logger.info("found in " + this.getClass().getName());
// return Response.ok(object).build();
// }
Customer customer = service.loadOneCustomer(new Integer(id));
// logger.info("nix found");
if(customer == null) {
throw new NotFoundException("No customer found with the matching ID: " + id);
}
logger.info("Customer found: " + customer.getCustomerNumber());
// return Response.status(Status.BAD_REQUEST).build();
return customer;
}
Client-Implementation:
public Response readCustomer(String id){
log.info("Starting: Rest get a Customer with ID: " + id);
log.info(this.URL);
this.customerWebTarget = this.client.target(this.URL).path(id);
Response response = this.customerWebTarget.request().buildGet().invoke();
// TODO Customer cast application_xml auf Customer? Customer customer = response.readEntity(Customer.class);
Customer customer = response.readEntity(Customer.class);
log.info("Ending: Rest invoque a Customer with ID:" + customer.getCustomerNumber());
// System.out.println("Ending: Rest get a Customer with ID: " + response.readEntity(String.class));
return response;
}
J-Unit Test:
#Test
public void testGetCustomerById() throws Exception {
Response response = this.customerRestClient.readCustomer("112");
System.out.println("--->" + response.getStatus());
Assert.assertTrue(response.getStatus() == 200);
}
Everything works perfekt till i try to get the Java-Object from the XML i get (Customer customer = response.readEntity(Customer.class);)
Am i missing something. I mean, i get read the xml-File and see every data in it... Why can't i cast it into Java-Object?
I always get this Error:
Javax.ws.rs.ProcessingException: Unable to find a MEssageBody of content-type-application/xml and type class de.....model.Customer
Without seeing the Customer class, it's hard to tell, but most likely some or all JAXB annotations are missing. In particular, you'll need an #XmlRootElement annotation.
Can you post the Customer class please. Was it properly annotated?
Also add the #Produces back in.