Error while casting in JPQL - jpa

This is on a JSF + JPA web application.
When I used the following JPQL query which include casting, it gives an error difficult to understand.
This is the query
jpql = "select new data.dataStructure.PharmacyStockRow(amp.vmp.name, sum(s.stock), "
+ "sum(s.itemBatch.purcahseRate * s.stock), sum(s.itemBatch.retailsaleRate * s.stock))"
+ "from Stock s join treat(s.itemBatch.item as Amp)amp "
+ "where s.stock>:z and s.department=:d "
+ "group by amp.vmp ";
m.put("d", department);
m.put("z", 0.0);
This is the class created just to receive the result from the query.
package data.dataStructure;
public class PharmacyStockRow {
String code;
String name;
Double qty;
Double purchaseValue;
Double saleValue;
public PharmacyStockRow() {
}
public PharmacyStockRow(String name, Double qty, Double purchaseValue, Double saleValue) {
this.name = name;
this.qty = qty;
this.purchaseValue = purchaseValue;
this.saleValue = saleValue;
}
I have a Stock class which stores data about stocks. It has a ItemBatch class which represent a batch. Batch has an Item. That item is actually a Amp, which extends Item. Amp has a Vmp. I want to get the stocks by Vmp. It would have been very easy if ItemBatch has a property of Amp, but I had to use more generic Item as the reference. So I need to cast Item to Amp within the Java Resistance Query. I tried it but it gives following error.
I use EclipseLink 2.5 as the Persistance Provider.
Caused by: Exception [EclipseLink-6034] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.QueryException
Exception Description: Invalid query item expression [
Query Key vmp
Query Key item (entity.pharmacy.Amp)
Query Key itemBatch
Base entity.pharmacy.Stock].
Query: ReportQuery(referenceClass=Stock jpql="select new data.dataStructure.PharmacyStockRow(amp.vmp.name, sum(s.stock), sum(s.itemBatch.purcahseRate * s.stock), sum(s.itemBatch.retailsaleRate * s.stock)) from Stock s join treat(s.itemBatch.item as Amp)amp where s.stock>:z and s.department=:d group by amp ")
at org.eclipse.persistence.exceptions.QueryException.invalidExpressionForQueryItem(QueryException.java:622)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.getLeafDescriptorFor(ObjectLevelReadQuery.java:1460)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.getLeafMappingFor(ObjectLevelReadQuery.java:1408)
at org.eclipse.persistence.internal.queries.ReportItem.initialize(ReportItem.java:124)
at org.eclipse.persistence.queries.ConstructorReportItem.initialize(ConstructorReportItem.java:137)
at org.eclipse.persistence.queries.ReportQuery.prepare(ReportQuery.java:1044)
at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:613)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.checkPrepare(ObjectLevelReadQuery.java:823)
at org.eclipse.persistence.queries.DatabaseQuery.prepareCall(DatabaseQuery.java:1741)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:268)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:190)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:142)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:126)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1475)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1497)

I failed to solved the issue, but did a work around. I add all the additional methods in the child class into the parent class so that the casting was not necessary.

Related

How to avoid N+1 problem with native SQL query in springboot with Hibernate?

I'm querying my DB with POSTGIS built-ins to retrieve the closest Machines given a Location.
I have to use a native SQL because Hibernate does not support POSTGIS and CTEs:
#Repository
public interface MachineRepository extends JpaRepository<Machine, Long>{
#Query(value =
"with nearest_machines as\n" +
" (\n" +
" select distance_between_days(:id_day, machine_availability.id_day) as distance_in_time,\n" +
" ST_Distance(geom\\:\\:geography, ST_SetSrid(ST_MakePoint(:longitude, :latitude), 4326)\\:\\:geography) as distance_in_meters,\n" +
" min(id_day) over (partition by machine.id) as closest_timeslot_per_machine,\n" +
" machine_availability.id_day,\n" +
" machine.*\n" +
" from machine\n" +
" join machine_availability on machine.id = machine_availability.id_machine\n" +
" where machine_availability.available = true\n" +
" and machine_availability.id_day >= :today\n" +
" and ST_DWithin(geom\\:\\:geography, ST_SetSrid(ST_MakePoint(:longitude, :latitude), 4326)\\:\\:geography, 1000)\n" +
" )\n" +
"select nearest_machines.*\n" +
"from nearest_machines\n" +
"where id_day = closest_timeslot_per_machine\n" +
"order by distance_in_time, distance_in_meters\n" +
"limit 20;",
nativeQuery = true)
List<Machine> findMachinesAccordingToAvailabilities(#Param("longitude") BigDecimal longitude,
#Param("latitude") BigDecimal latitude,
#Param("id_day") String idDay,
#Param("today") String today);
}
Of course, Machine and MachineAvailability are #Entity's. And they are #OneToMany(fetch = FetchType.EAGER) related. I changed the default LAZY to EAGER cause i need the MachineAvailability in the final JSON.
The problem is that it triggers 2 more requests by resulting machine(ie the famous N+1 problem).
1.How can i solve that in only ONE request?
2.Is it possible to create my on JSON somehow and returning it directly in the MachineController?
Solving this in 1 request is tough as you will have to use Hibernate native APIs to map the table aliases for the availability collection. You would need to add a join for the availabilities in the main query and do something like this: session.createNativeQuery("...").addEntity("m", Machine.class).addFetch("av", "m", "availabilities")
Another alternative would be to use Blaze-Persistence Entity Views since Blaze-Persistence comes with support for CTEs and many more goodies that PostgreSQL provides, this might be an interesting solution for you.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
I don't know your model, but a possible DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(Machine.class)
#With(NearestMachineCteProvider.class)
#EntityViewRoot(name = "nearest", entity = NearestMachine.class, condition = "machineId = VIEW(id)", joinType = JoinType.INNER)
public interface MachineDto {
#IdMapping
Integer getId();
String getName();
#Mapping("nearest.distanceInTime")
Integer getDistanceInTime();
#Mapping("nearest.distanceInMeters")
Double getDistanceInMeters();
Set<MachineAvailabilityDto> getAvailabilities();
#EntityView(MachineAvailability.class)
interface MachineAvailabilityDto {
#IdMapping
Integer getId();
String getName();
}
class NearestMachineCteProvider implements CTEProvider {
#Override
public void applyCtes(CTEBuilder<?> builder, Map<String, Object> optionalParameters) {
builder.with(NearestMachine.class)
.from(Machine.class, "m")
.bind("distanceInTime").select("CAST_INTEGER(FUNCTION('distance_between_days', :id_day, m.availabilities.idDay))")
.bind("distanceInMeters").select("CAST_DOUBLE(FUNCTION('ST_Distance', m.geom, FUNCTION('ST_SetSrid', FUNCTION('ST_MakePoint', :longitude, :latitude), 4326)))")
.bind("closestTimeslotId").select("min(m.availabilities.idDay) over (partition by m.id)")
.bind("machineId").select("m.id")
.bind("machineAvailabilityDay").select("m.availabilities.idDay")
.where("m.availabilities.available").eqLiteral(true)
.where("m.availabilities.idDay").geExpression(":today")
.where("FUNCTION('ST_DWithin', m.geom, FUNCTION('ST_SetSrid', FUNCTION('ST_MakePoint', :longitude, :latitude), 4326), 1000)").eqLiteral(true)
.end();
}
}
}
#CTE
#Entity
public class NearestMachine {
private Integer distanceInTime;
private Double distanceInMeters;
private Integer closestTimeslotId;
private Integer machineId;
private Integer machineAvailabilityDay;
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
MachineDto a = entityViewManager.find(entityManager, MachineDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<MachineDto> findAll(Pageable pageable);
You can then sort by using Sort.asc("distanceInTime") and Sort.asc("distanceInMeters")
The best part is, it will only fetch the state that is actually necessary!

How to map ALL names directly by JPA?

Given a ZIP-code-like hierarchical code/name schema.
For example:
code = 101010
Code:
100000 level 1 code (10....)
101000 level 2 code (..10..)
101010 level 3 code (....10)
Name (short name)
100000 - A
101000 - a
101010 - i
Name (FullQualifiedName)
100000 - A
101000 - A->a
101010 - A-a->i
EDIT
I wanna following code (JPA pseudo code), but CANNOT.
#Entity
public class CodeName{
// ....
String code; // 100101 levels = {100000, 100100, 100101}
String name; //
#HowToMapDirectedToNameOfCode('100000') // #SecondTable ?
String name1;
#HowToMapDirectedToNameOfCode('100100')
String name2;
#HowToMapDirectedToNameOfCode('100101')
String name3;
String getFullQualifiedName(){
return String.format("%s->%s->%s", name1, name2, name3);
}
// getter and setter
}
But it's relatively easier in native SQL:
SELECT (select p1.name from codename p1 where p1.code= concat( substring(p.code,1,2), "0000") ) province,
(select p2.name from codename p2 where p2.code= concat( substring(p.code,1,4), "00") ) city,
(select p3.name from codename p3 where p3.code=p.code) area
FROM codename p WHERE p.code = '100101';
So, I implements it as following snippet.
#Entity
public class CodeName{
// ....
String code; // 100000, 101000, 100101
String name; // province, city , area
#Transient
String name1; // mapping directly?
#Transient
String name2; // mapping directly?
#Transient
String name3; // mapping directly?
String getFullQualifiedName(){
return String.format("%s->%s->%s", name1, name2, name3);
}
// getter and setter
}
public interface CodeNameRepository extends CrudRepository<CodeName, Long>, CodeNameRepositoryCustom {
#Query(" FROM CodeName p " +
" WHERE p.code = CONCAT(SUBSTRING(?1, 1, 2), '0000') " +
" OR p.code = CONCAT(SUBSTRING(?1, 1, 4), '00') " +
" OR p.code = ?1")
List<CodeName> findAllLevelsByCode(String code);
}
#Component
public class CodeNameRepositoryImpl implements CodeNameRepositoryCustom {
#Autowired
private CodeNameRepository codeNameRepository ;
#Override
public CodeName CodeNamefindFullQualifiedNameByCode(String code) {
List<CodeName> codeNames= codeNameRepository .findAllLevelsByCode(code);
CodeName codeName;
// extra name1, name2, name3 from list,
// fill code, name, name1, name2, name3 to codeName and
return codeName;
}
}
But it have SO MANY limitations.
Most likely, I need getFullQualifiedName(), to display it on UI, but every time I must have an extra call to populate all names.
For each entity has CodeName as its children, no matter how deep the codeName is at, I MUST expand to the codeName and reload it with FQN.
Can we mapping all #Transient names directly by JPA?
You could technically model your code repository entity as follows:
public class CodeName {
#Id
#GeneratedValue(GenerationStrategy.AUTO)
#Column
private Long id;
#ManyToOne
private CodeName parent;
#OneToMany(mappedBy = "parent")
private List<CodeName> children;
#Column
private String name;
#Transient
public String getFullyQualifiedName() {
List<String> names = new ArrayList<>();
names.add(name);
CodeName theParent = parent;
while(theParent != null) {
names.add(theParent.getName());
theParent = theParent.parent;
}
Collections.reverse(names);
return StringUtils.join(names, "->");
}
}
Because the parent relationships will be fetched EAGERLY because they mapped as #ManyToOne, you can basically start at any child CodeName entity and traverse up it's parent/child relationship to the root. This basically allows the getFullyQualifiedName method to build the name for you at runtime.
If performance becomes a problem doing this, you can always datamine the names ahead of time in your entity as you described by adding a #Column private String fullyQualifiedName and make sure that field is inserted when you create your codes. Then the transient method I added to my the entity can be dropped since you're caching the names at data insertion.
It is possible to write a JPQL, which is equivalent to your SQL query. The only tricky part is to rewrite nested selects into cross joins, because nested selects are not supported by JPA and you need to join unrelated entities. On the other hand, functions CONCAT and SUBSTRING are supported by JPQL in the same way as in SQL. See the following JPQL query, which should give you the results as the SQL query in the question:
SELECT p1.name // province
, p2.name // city
, p.name // area
FROM CodeName p, CodeName p1, CodeName p2
WHERE p.code = '100101'
AND p1.code = concat( substring(p.code,1,2), "0000")
AND p2.code= concat( substring(p.code,1,4), "00")
The above query will give you 3 values in one row, which cannot be mapped into a single entity. The result of the query will therefore be a list of Object[] arrays. You may also add the original entity into the select clause: SELECT p1.name, p2.name, p.name, p FROM .... This way, you may later process the list of results and assign first three values into the transient fields of the entity:
Object[] rows = query.getResultList();
for (Object row : rows) {
CodeName c = (CodeName)row[3];
c.setName1((String)row[0]);
c.setName2((String)row[1]);
c.setName3((String)row[2]);
}

JPA 2.0 IllegalArgumentException on existing entities and populated DB value

I am trying to return a string from a table based on a conditional ID (subid) from an already populated table. The query should return a list of type ItemDataPoint entity. In a JSF managed bean, the list will the be iterated by a an enhaned for loop. If the word "Include" is found by the loop, the method will create a specific type of chart. In simpler terms, I want to return a string based the ID condition being met. I am getting:
javax.ejb.EJBException
at com.sun.ejb.containers.EJBContainerTransactionManager.processSystemException
(EJBContainerTransactionManager.java:748)
at com.sun.ejb.containers.EJBContainerTransactionManager.
completeNewTx(EJBContainerTransactionManager.java:698)
at com.sun.ejb.containers.EJBContainerTransactionManager.postInvokeTx
(EJBContainerTransactionManager.java:503)
at com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:4475)
at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:2009)
at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1979)
Caused by: java.lang.IllegalArgumentException: You have attempted to set
a parameter at position 2 which does not exist in this query string SELECT p FROM
Itemdatapoint p JOIN p.series s WHERE s.master.item.subs.subid = :subid.
at org.eclipse.persistence.internal.jpa.QueryImpl.setParameterInternal(QueryImpl.java:925)
at org.eclipse.persistence.internal.jpa.QueryImpl.setParameterInternal(QueryImpl.java:906)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.setParameter(EJBQueryImpl.java:469)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.setParameter(EJBQueryImpl.java:1)
at com.manaar.clientmods.gazprom.design3.data.facade.ItemdatapointFacade.
chartType(ItemdatapointFacade.java:78)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
The subid value of 2 exists if I run a native SQL query on the relevant parent table in the DB. Also the type of the subid is an int in the main entity class, the JPQL Facade class and the managed bean.
The JPQL:
public List<Itemdatapoint> chartType (int subid) {
Query q = em.createQuery("SELECT p FROM Itemdatapoint p JOIN p.series s WHERE s.master.item.subs.subid = :subid");
q.setParameter(subid, "subid");
return q.getResultList();
}
The managed bean:
#Named(value = "reportBean")
#SessionScoped
public class ReportBean implements Serializable {
#Inject
private ItemdatapointFacade facade;
public String typeSwitch1() {
subid = 2;
chartType = facade.chartType(subid);
for(Itemdatapoint e: chartType) {
status = e.getSeries().getMaster().getStatus();
if(status.equals("Include")) {
return "line";
}
}
return null;
}
The xhtml page:
<p:chart type="#{reportBean.typeSwitch1()}" model="#{reportBean.subLineChart1}"/>
I also tried a non Join JPQL just from a single table:
public List<Itemdatapoint> noJoin (int subid) {
Query q = em.createQuery("SELECT p FROM Itemdatapoint p WHERE p.pointid = :subid");
q.setParameter(subid, "subid");
return q.getResultList();
}
Similar problem:
java.lang.IllegalArgumentException: You have attempted to set a
parameter at position 2 which does not exist in this query string
SELECT p FROM Itemdatapoint p WHERE p.pointid = :subid.
I gather that IllegalArgumentException means that the selected entity does not exist or is not the correct type consistent with the query string in the facade class. But in my case the entity exists and the parameter is the correct type.
I would appreciate any help in understanding why i'm getting this error. Thank in advance!
UPDATE
Responding to the answer from lametaweb, I want to better understand the concept of JPA parameters.
According to the JPA documentation, the first argument of the setParameter method is the parameter name or number. The second argument is the object that should be bound to the named parameter. Why does the following work without throwing Illegal ArgumentException?
I tested an xhtml (web page):
<p:dataGrid id="rep1" columns="1" value="#{pageBean.itemPageList1}" var="items1" rows="4">
<p:commandLink value="#{items1.itemname}" action="#{pageBean.showItem1}" ajax="false"/>
</p:dataGrid>
The bean code:
public ListDataModel<Sectionitem> getItemPageList1() {
subid = 1;
reportStatus = "Include";
itemPageList1 = itemFacade.viewItems(subid, reportStatus);
return itemPageList1;
}
The JPA facade:
public ListDataModel<Sectionitem> viewItems(int subid, String stat) {
Query q = em.createQuery("select s from Sectionitem s JOIN s.subs c where c.subid = :subid AND s.status = :stat ORDER BY s.daterec");
q.setParameter("subid", subid);
q.setParameter("stat", stat);
ListDataModel<Sectionitem> res
= new ListDataModel<Sectionitem>(q.getResultList());
return res;
}
Why is it in this case, the object exist but in my original case the subid object does not exist?
You are invoking this method in your code:
setParameter(int position, Object value)
but you have to invoke this one instead:
setParameter(String name, Object value)
So your code should be:
q.setParameter("subid", Integer.valueOf(subid));
But, if you invoke:
q.setParameter(subid, "subid");
here the first parameter represents the position of the argument and the second the value for it. So you are passing a value of "subid" for the parameter in the second (2) position, which doesn't exist, because you only have one parameter in your JPQL query, hence the IllegalArgumentException exception.
Note: Why do you have a primitive type in your entity? Why not an Integer instead an int?

How to use MySQL's full text search from JPA

I want to use MySQL's full text search features using JPA, without having to use a native query.
I am using EclipseLink, which has a function to support native SQL commands: FUNC. However, the help examples only show this being use with simple MySQL functions. My best effort attempt to get it to work with MATCH & AGAINST is as follows:
#PersistenceContext(name="test")
EntityManager em;
Query query = em.createQuery("SELECT person FROM People person WHERE FUNC('MATCH', person.name) FUNC('AGAINST', :searchTerm)");
...
query.getResultList();
Which gives the following exception:
Caused by: NoViableAltException(32#[()* loopback of 822:9: (m= MULTIPLY right= arithmeticFactor | d= DIVIDE right= arithmeticFactor )*])
at org.eclipse.persistence.internal.libraries.antlr.runtime.DFA.noViableAlt(DFA.java:159)
at org.eclipse.persistence.internal.libraries.antlr.runtime.DFA.predict(DFA.java:116)
at org.eclipse.persistence.internal.jpa.parsing.jpql.antlr.JPQLParser.arithmeticTerm(JPQLParser.java:4557)
... 120 more
I am open to alternatives other that using the FUNC method.
I am using EJB 3 and EclipseLink 2.3.1.
An improved answer of #Markus Barthlen which works for Hibernate.
Create custom dialect
public class MySQLDialectCustom extends MySQL5Dialect {
public MySQLDialect() {
super();
registerFunction("match", new SQLFunctionTemplate(StandardBasicTypes.DOUBLE,
"match(?1) against (?2 in boolean mode)"));
}
}
and register it by setting hibernate.dialect property.
Use it
in JPQL:
Query query = entityManager
.createQuery("select an from Animal an " +
"where an.type = :animalTypeNo " +
"and match(an.name, :animalName) > 0", Animal.class)
.setParameter("animalType", "Mammal")
.setParameter("animalName", "Tiger");
List<Animal> result = query.getResultList();
return result;
or with Criteria API:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Animal> criteriaQuery = criteriaBuilder.createQuery(Animal.class);
Root<Animal> root = criteriaQuery.from(Animal.class);
List<Predicate> predicates = new ArrayList<>();
Expression<Double> match = criteriaBuilder.function("match", Double.class, root.get("name"),
criteriaBuilder.parameter(String.class, "animalName"));
predicates.add(criteriaBuilder.equal(root.get("animalType"), "Mammal"));
predicates.add(criteriaBuilder.greaterThan(match, 0.));
criteriaQuery.where(predicates.toArray(new Predicate[]{}));
TypedQuery<Animal> query = entityManager.createQuery(criteriaQuery);
List<Animal> result = query.setParameter("animalName", "Tiger").getResultList();
return result;
Some more details in this blog post: http://pavelmakhov.com/2016/09/jpa-custom-function
FUNC only works with normal printed functions,
i.e.
MATCH(arg1, arg2)
since MATCH arg1 AGAINST arg2 is not printed the way a function is normally printed, FUNC cannot be used to call it.
EclipseLink ExpressionOperators do support printing functions like this, so you could define your own ExpressionOperator, but ExpressionOperators are only supported through EclipseLink Expression queries currently, not through JPQL. You could log an enhancement to have operator support in JPQL.
You could also use a native SQL query.
Just to complete the answer: I had the same problem, but using the criteria builder. This is how you can get around the limitations in the standart implementation, if you are using EclipseLink:
Cast JPA expression to EclipseLink expression
Use the sql method
If you match against a compound index, create it using the function method
Example:
JpaCriteriaBuilder cb = (JpaCriteriaBuilder) cb;
List<String> args = new ArrayList();
args.add("Keyword");
Expression<Boolean> expr = cb.fromExpression (
cb.toExpression(
cb.function("", String.class,
table.get(Table_.text1), table.get(Table_.text2))
)
.sql("MATCH ? AGAINST (?)", args)
);
query.where(expr);
If you need to cast the expression to a predicate use the following:
query.where( cb.gt(expr, 0));
What about new SQL operator in EclipseLink 4.0? I think it can help you to do fulltext search from JPQL. But you have to upgrade to EclipseLink 4.0.
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/Support_for_Native_Database_Functions#SQL
Edit:
Sorry for late update.
Verified correct use of EclispeLink 2.4.0 "SQL" operator with MySQL fulltext search is
SELECT person FROM People person WHERE SQL('MATCH(name) AGAINST( ? )', :searchTerm)"
where name is column on which Fulltext index is defined. :searchTerm is string you use for searching.
Works without problems.
To elaborate on the answer of James:
It seems like I had luck extending the mysql dialect using
registerFunction("match", new SQLFunctionTemplate(DoubleType.INSTANCE, "match(?1) against (?2 in boolean mode)"));
and invoking the function via the following jpql fragment
match(" + binaryDataColumn + ",'" + StringUtils.join(words, " ") + "') > 0
I had to guess the return type, but this should get you started.
FInally work
if you set your table colums wit index full search
#NamedNativeQuery(name = "searchclient",
query = "SELECT * FROM client WHERE MATCH(clientFullName, lastname, secondname, firstphone,"
+ " secondphone, workphone, otherphone, otherphone1,"
+ " otherphone2, detailsFromClient, email, company,"
+ " address, contractType, paymantCondition) AGAINST(?)",
List list = em.createNamedQuery("searchclient").setParameter(1, searchKey).getResultList();
The simplest variant is to use NativeQuery
Example of use it with mapping to JPA entity (FiasAddress):
public class FiasServiceBean implements FiasService {
#PersistenceContext(unitName = "fias")
EntityManager entityManager;
#Override
public Collection<FiasAddress> search(String name, int limit, int aolevel) {
Query query = entityManager.createNativeQuery(
"SELECT fa.* FROM fias.addressobject fa" +
" WHERE MATCH(FORMALNAME) AGAINST (:name IN NATURAL LANGUAGE MODE)" +
" AND AOLEVEL = :AOLEVEL" +
" LIMIT :limit",
FiasAddress.class
);
query.setParameter("name", name);
query.setParameter("limit", limit);
query.setParameter("AOLEVEL", aolevel);
Iterator iterator = query.getResultList().iterator();
ArrayList<FiasAddress> result = new ArrayList<>();
while (iterator.hasNext()) {
result.add((FiasAddress) iterator.next());
}
return result;
}
}

jpa call readonly composite table but getting "Exception Description: Missing descriptor for [CollectorInfo]"

In a Spring 3 app a controller is calling a JpaCollectorManager with calls a JpaCollectorInfoDao to get a list which is defined by a nativequery. The query calls 2 seperate tables which uses sql and jpql because I need to use a postgresql feature not implemented in jpql. When the controller tries to file the list I get the following error message:
Exception [EclipseLink-6007] (Eclipse Persistence Services - 2.1.2.v20101206-r8635): org.eclipse.persistence.exceptions.QueryException
Exception Description: Missing descriptor for [CollectorInfo].
Query: ReadAllQuery(referenceClass=CollectorInfo sql="select distinct ON ( col.collector_id,pst.process_source_type ) col.*,pst.process_source_timestamp,pst.process_source_type from perform_schema.collector col join perform_schema.process_set pst on pst.collector_id = col.collector_id order by col.collector_id, pst.process_source_type,pst.process_source_timestamp desc ")
The controller Java has the following call:
List<CollectorInfo> ps = this.collectorInfoManager.getLatestCollectorInfo();
The JpaCollectorInfoManager.java has this:
public List<CollectorInfo> getLatestCollectorInfo()
{
return collectorInfoDao.getLatestCollectorInfo();
}
The JpaCollectorInfoDao.java:
#Override
#Transactional
public List<CollectorInfo> getLatestCollectorInfo() {
Query query = entityManager.createNativeQuery( ( "select distinct ON ( col.collector_id," +
"pst.process_source_type ) " +
"col.*," +
"pst.process_source_timestamp," +
"pst.process_source_type " +
"from perform_schema.collector col " +
"join perform_schema.process_set pst " +
"on pst.collector_id = col.collector_id " +
"order by col.collector_id, " +
"pst.process_source_type," +
"pst.process_source_timestamp desc " ),
CollectorInfo.class );
return ( (List<CollectorInfo>) query.getResultList() );
}
The CollectorInfo class does not have an #Entity defined. If I set the #Entity defined then it tells me that the Table cannot be resolved (which is correct since the there is no actual table). I have tried all sorts of permutations and cannot seem to make this needle thread.
Not sure what you are trying to do exactly?
You need to map the class as an Entity in order to be able to select instances of it.
Either, do not include the class, the native SQL query will an Object[] of the data, which you can map in your own code to your class.
Or map it as an Entity excepting the data that you are returning. The #Table will not be relevant as you are mapping the object to the query results. This should not cause any errors though, unless you are auto creating table or using integrity checker.
Or map the objects to the table correctly. Then use a fetch join, or batch fetch to optimize your retrieval if required.