partially mapping the result of a NamedNativeQuery to a class - java

I have an #Entity class Person, that has multiple fields and I would like to map the result of several #NamedNativeQuerys to the Person class however, the queries I am running do not return values for every field in the Person class. When I try to run a query I get the following errors:
[error] o.h.u.JDBCExceptionReporter - Invalid column name bar.
[error] play - Cannot invoke the action, eventually got an error: javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not execute query
My class is set up similar to this:
#NamedNativeQueries({
#NamedNativeQuery(
name = "getBirthdate",
//Returns values for {idnumber, name, birthdate}
query = "EXEC dbo.proc_get_birthdate :name",
resultClass = Person.class
),
#NamedNativeQuery(
name = "getBar",
//Returns values for {idnumber, name, bar}
query = "EXEC dbo.proc_get_bar :name",
resultClass = Person.class
)
})
#Entity
public class Person {
#Id
#Column(name = "idnumber")
private int idNumber;
#Column(name = "name")
private String name;
#Column(name = "birthdate")
private String birthdate;
#Column(name = "foo")
private String foo;
#Column(name = "bar")
private String bar;
//All appropriate getter and setter methods are implemented
}
I double checked and all the columns in the Person class do, in fact, exist in the table being queried.
My actual class is much larger and due to that and some security concerns, I do not want my queries to have to return EVERY field and am hoping that there is a simple way to just give them a value of null if a value isn't returned by the query. I tried to set each field to null in the declaration (example below) but that didn't work either.
#Column(name = "bar")
private String bar = null;
I would really rather not have to create a tailored class for every single query I need to run so if what I'm trying to do is possible, any help would be greatly appreciated. Thanks in advance.

I believe that you misunderstood the #NamedNativeQuery use. This annotation should be used with a SQL Query and not with a stored procedure. Looking at the exception SQLGrammarException: could not execute query it is possible to see that is a grammar exception, the query could not be parsed.
With JPA 2.1 you could use the annotation #NamedStoredProcedureQuery that is the one with support to stored procedures.

Related

Hibernate Query for Object based on key value pairs of the Object's Java Map<String, String>

I am trying to query for a Java Object using HQL which is performing filters based on the Object's Java Map that it has.
Essentially what I want to ask is 'give me all the error reports where mapkey1=val_x and mapkey2=val_y'
I have this object (stripped down)
#Entity
#Table(name = "error_report")
public class ErrorReport implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "id", length=50)
private String id= UUID.randomUUID().toString();
#ElementCollection
#CollectionTable(name = "error_property", joinColumns = {#JoinColumn(name = "error_id", referencedColumnName = "id")})
#MapKeyColumn(name = "prop", length=50)
#Column(name = "prop_val")
#Type(type="text")
private Map<String, String> reportedProperties = new HashMap<>();
}
So I want ErrorReports based on the reportedProperties. I have set up a unit test and everything works perfectly when the reportedProperties Map has only 1 entry per ErrorReport. This is the HQL I used:
from ErrorReport as model where KEY(model.reportedProperties) = :A1 and VALUE(model.reportedProperties) = :A2
When the ErrorReport has 2 entries in the reportedProperties Map the query fails with the following error:
could not extract ResultSet
caused by
HsqlException: cardinality violation
When I look at the generated SQL and try and run it manually I can see it will not work, because the innter select is returning multiple results.
SELECT error_report_.id AS id1_2_, error_report_.product_url AS product_2_2_, error_report_.audit_id AS audit_id3_2_, error_report_.category_id AS
category4_2_, error_report_.error_desc AS error_de5_2_, error_report_.notifier_id AS notifier6_2_, error_report_.product_name AS
product_7_2_, error_report_.product_version AS product_8_2_, error_report_.error_time AS error_ti9_2_
FROM error_report error_report_
CROSS JOIN error_property reportedpr1_
CROSS JOIN error_property reportedpr2_
WHERE error_report_.id=reportedpr1_.error_id
AND error_report_.id=reportedpr2_.error_id
AND reportedpr1_.prop=?
AND
(SELECT reportedpr2_.prop_val FROM error_property reportedpr2_ WHERE error_report_.id=reportedpr2_.error_id)=?
Clearly there is something wrong with my HQL, but it seems to follow other examples I have found. Does anyone know what the syntax is?
I am using hibernate 5.4.9.Final
For anyone in the future, I had similar problem and solved it by applying join on the collection table combined with INDEX().
SELECT DISTINCT(model.id), model.product_url, ...
FROM ErrorReport as model
...
JOIN model.reportedProperties rProp
WHERE INDEX(rProp) = :A1 AND rProp = :A2
Here the INDEX(rProp) is the key and rProp is the value. Also DISTINCT was needed because it was returning duplicate records for me due to the map.

Insert attribute in table if it is not null using Hibernate

I have mapping class defined as:
#Table(name = "TEST_TABLE")
public class DBTestAccount
{
#Id
#Column(name = "UUID", nullable = false, length = 36)
private String uuid;
#Column(name = "REGION")
private String region;
#Column(name = "COUNTRY")
private String countryCode;
//getters and setters
}
Now I need to update the table. For that let's say I create following object:
DBTestAccount dbTestAccount = new DBTestAccount();
dbTestAccount.setUuid("testUUID");
dbTestAccount.setRegion("testRegion");
dbTestAccount.setCountryCode(null);
Now let's say initially in the table we have a record that has some value of COUNTRY. Inserting the above object will replace the value and make COUNTRY null. I want that it should update the data, but if the column is null, then it should ignore and do not update it. If it is non-null then it should update it. How to achieve this in hibernate? Is there an annotation to do so? If not then what is the possible solution (except using if - else). Can I create a custom annotation for this?
PS:
The underlying database is PostgreSQL.
The example you are describing can't be present in the database, because the object is not an entity yet, as it is created with new keyword and it isn't yet persisted in the database.
From your explanation, what I got, is that you want to save only changed attributes. For that purpose hibernate has the Dynamic Update annotation.

Hibernate entity join substitute value2 when value1 NULL

I am using Hibernate and trying to build the next logic in my entity for SELECT query. Creating a join column where if value of professor's name = NULL, then select value of teacher's name.
Code for Teacher table:
#Entity
#Table(name = "teacher")
public class Teacher {
#Id
#Column(name = "id_number)
private String id;
#OneToOne
#JoinColumn(name = "t_name")
private Professor name;
// Getters and Setters ...
}
Code for Professor table:
#Entity
#Table(name = "professor")
public class Professor{
#Id
#Column(name = "id_number)
private String id;
#Column(name = "p_name")
private String name;
// Getters and Setters ...
}
Working SQL query example:
select
t.id_number as "Identification Number",
isnull(p.p_name, t.t_name) as "Name"
from teacher t
left join professor p
on t.t_name = p.p_name
where id_number in (23, 24, 25, 26, 27)
What should I change in my entities to replicate logic of the SQL query above? Will really appreciate for any help provided.
I'm not sure if you can provide an annotation at field (name) level to achieve this. My guess is, if something like that is present then it might cause the update also to behave the same way. (override teacher's name with professor's)
Couple of other solutions:
Hibernate's Formula annotation:
Create another variable say actualName and provide Formula Annotation with Coalesce ( I used it before to return another field when one field was null).
#Formula("COALESCE(nullableField, backupField)")
I'm not sure if you can use a mapped entity in it, if not you've to make use of JoinColumnOrFormula annotation and write a query for this.
Create a getter for this new field actualName which will check if professor's name is present then return it. else return teacher's name. This will eliminate the need to write another query.
You could also modify the getter of name field in teacher class to return another field that you would want. NOTE: This will also cause your update operation on teacher's table to replace teacher's name with professor's if professor's name is present. Not Recommended at all

JPQL in Google App Engine - Cannot find type of(part of) x since symbol has no type; implicit variable?

I have a web app hosted on Google App Engine with a datastore containing some tables. The table I'm querying is Towns. I want to get all towns of x name. In the datastore, I can see the following columns: ID/Name, date, town.
#Entity
public class Town {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
private String Name;
private String Date;
// getters, setters
}
My query is as follows:
EntityManager em = EMF.get().createEntityManager();
Query q = em.createQuery("SELECT t FROM Towns t WHERE t.name LIKE :townName ORDER BY c.date")
.setParameter("townName", name);
List<Town> townLogs = new ArrayList<Town>(q.getResultList());
However, I get the following error:
org.datanucleus.exceptions.NucleusUserException: Cannot find type of (part of) t.name since symbol has no type; implicit variable?
Change the case of Name to name & Date to date. That should help you get around the issue that you are facing.
The next issue that you are likely to face is with the implicit reference of "c". That should also ideally be t.date
Hope that helps.

Native Query (JPA ) not reset and return the same old result

I have a native sql query as the following :
for (init i=0; i<=2 ; i++) {
String sql = "Select * from accounts where id = ?";
Query query = em.createNativeQuery(sql,AccountBean.class);
query.setParameter(1, i );
AccountBean accountBean = (AccountBean)query.getSingleResult();
}
For the first loop it works correctly but any loop after the first one returns the same result as the first one , i debug it, the parameter changed , it works correctly if i change
Query query = em.createNativeQuery(sql,AccountBean.class);
to
Query query = em.createNativeQuery(queryString);
Regards
Wish79
Every JPA entity must have a primary key. Your JPA entities may not properly reflect the primary key, if any, on the database table.
I ran into the same problem. In my model class I had only one class variable annotated with #Id. However, that was not an accurate reflection of the table itself, which has a composite primary key. Thus, my query results returned the correct number of rows, but each confoundingly contained the same values, even though the actual data was different in the db. For example, this query:
Query query = entityManager.createQuery
("SELECT tbl FROM Tbl tbl WHERE tbl.id = 100
and tbl.code in ('A','B','C')");
...returned 10 rows, each showing a code of 'A'. But in actuality 9 of those 10 rows had a different code value ('B' or 'C'). It seemed as if the results were being cached and/or the tbl.code predicate was ignored. (That happened whether I used JPQL or Native SQL.) Very confusing.
To fix this I added an additional #Id annotation to my model to reflect the composite primary key:
#Id
#Column(name = "Code")
public String getCode() {
return this.code;
}
Now the query returns the data correctly and the code select criteria is no longer effectively ignored.
Edit: Although the above worked for me, on further research it seems a better approach to configure a separate JPA Entity composite primary key class. See http://docs.oracle.com/cd/E16439_01/doc.1013/e13981/cmp30cfg001.htm.
For example, here's an Entity class with an embedded primary key (see #EmbeddedId):
/**
* The persistent class for the SOME_TABLE database table.
*/
#Entity
#Table(name = "SOME_TABLE")
public class SomeTable implements Serializable {
#EmbeddedId
private SomeTablePk id;
#Column(name = "NUMBER_HRS")
private BigDecimal numberHrs;
...
...and here's the composite primary key class (see #Embeddable):
#Embeddable
public class SomeTablePk implements Serializable {
#Column(name = "SOME_ID")
private String someId;
#Column(name = "ANOTHER_ID")
private BigDecimal anotherId;
public String getSomeId() {
return someId;
}
...

Categories

Resources