In my WAS application, I have a requirement to define a variable(String) in an Entity class that maps to a table.
So the fields that are related to the table have annotation as #Column(name="column_name")
I have a requirement to add a new field/variable to this Entity class that is not a column in table. If I declare it as a normal field, JPA converts this field also in the SQLs. This is causing SQL -206 error(as expected).
How to do I declare this field? Is there an annotation that I can use to say its a custom variable and not related to any of the columns in the table defined by this Entity?
example:
#Entity
#Table(name = "TABLE_1")
#NamedQueries(
{
#NamedQuery(name = "abc:findTableContent", query = "SELECT u FROM TABLE_1 u WHERE u.Id = :iD"),
})
public class TableEntity1 implements Serializable
{
#Id
#Column(name = "TABLE1_ID")
private Long iD;
#Column(name = "DESC")
private String desc;
private String error;
}
So if I run the namedquery, it gets executed as "SELECT t0.iD, t0.desc, t0.error FROM TABLE_1 u WHERE u.iD=?"
How do I solve this? Thanks for your help!
I found the answer. I could mark the field or variable as #javax.persistence.Transient
Related
I got an error when trying to do a request with JPA.
I have specified in my class entity, the schema where the table is :
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(schema = "dwp_schema")
public class Corridor {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id_corridor;
private Integer id_floor;
private String orientation;
}
And when I am doing a specific request in my repository :
public interface CorridorRepository extends JpaRepository<Corridor, Integer> {
#Query(value = "select * from corridor c inner join floor f on f.id_floor=c.id_floor INNER JOIN building b on f.id_building = b.id_building WHERE b.building_name=?1 AND f.floor_number=?2" ,nativeQuery = true)
List<Corridor> getCorridorsByFloor(String building_name, int floor);
}
I have the following error in Postgres :
org.postgresql.util.PSQLException: ERROR: relation "corridor" does not exist
Does someone have an idea ?
Thank you.
You need to specify your DB and Schema in the connection string:
jdbc:postgresql://localhost:5432/dbname?currentSchema=dwp_schema
Putting schema name right in the entity declaration would be a bad idea anyway - your DBA should be able to decide on schema names.
Try write schema name before table name:
#Query(value = "select * from dwp_schema.corridor c inner join floor f on f.id_floor=c.id_floor INNER JOIN building b on f.id_building = b.id_building WHERE b.building_name=?1 AND f.floor_number=?2" ,nativeQuery = true)
Group by entities is possible in HQL, but it seems that by entities mapped with joined inheritance doesn't.
select a, count(r) from Root r join r.a a group by a
Executing the above query results in a sql with insufficient columns in group by:
select
suba1_.id as col_0_0_,
count(root0_.id) as col_1_0_,
suba1_.id as id1_12_,
suba1_1_.super_data as super_da2_12_,
suba1_.sub_data as sub_data1_10_
from root root0_
inner join suba suba1_ on root0_.a_id=suba1_.id
inner join supera suba1_1_ on suba1_.id=suba1_1_.id
group by suba1_.id
Which gives the following error message:
o.h.engine.jdbc.spi.SqlExceptionHelper: expression not in aggregate or GROUP BY columns: SUPERA1_.SUPER_DATA in statement
The entities are described as followed:
#Entity
public class Root {
private #Id Integer id;
#ManyToOne
private SubA a;
}
#Entity
public class SubA extends SuperA {
private String subData;
}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class SuperA {
private #Id Long id;
private String superData;
}
Also if changed the type of Root::a to SuperA, the sql generated by the hql will have the same problem.
So is group by entities with joined inheritance type possible or am I missing something?
PS. This hql query does work in case if SuperA is mapped with table per class inheritance type but then fails with the same problem if type of Root::a is changed to SuperA as well.
Hibernate version: org.hibernate:hibernate-core:jar:5.4.32.Final:compile
Yes, with a bit of trickery, to anwser my own question. The missing column for the group by in the sql is the column id of SuperA, so the solution would be to include it. I found that possible by mapping the superclass's id with an extra column and reference it in the group by along side the entity.
So, in SuperA an extra column mapping superId would be added:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class SuperA {
private #Id Long id;
#Column(name="id", updatable = false, insertable = false)
private Long superId;
private String superData;
}
And referenced in the group by
select a, count(r) from Root r join r.a a group by a, a.superId
This solution was tested only for HSQLDB and PostgreSQL.
I have the following problem. I want to execute this query in my spring boot project. I tried to do this with the query annotation in the JPA repository interface. But it says "unexpected SELECT" at the inner join. When I execute this query directly on my mySQL database, it will work.
Do anyone have a solution for this case?
This is my query:
SELECT t1.*
FROM az_manager t1
INNER JOIN
(
SELECT maID, MAX(datum) AS max_date
FROM az_manager
WHERE maID IN (7243, 1)
GROUP BY maID
) t2
ON t1.maID = t2.maID AND t1.datum = t2.max_date
WHERE
t1.maID IN (7243, 1);
This is my class:
#Entity
#Table(name = "az_manager")
#IdClass(TnsWorkingHoursManagerId.class)
#Getter
#Setter
public class TnsWorkingHoursManager extends TnsObject{
#Id
#Column(name = "datum")
private long date;
#Id
#Column(name = "maid")
private int employeeId;
#Column(name = "typid")
private int typeId;
#Column(name = "bemerkung")
private String comment;
#Column(name = "host")
private String host;
#Column(name = "modus")
private byte mode;
public TnsWorkingHoursManager() {
}
}
Here is my try with the JPA repository:
#Query(value = "SELECT azm1 FROM az_manager azm1 INNER JOIN (SELECT maID, MAX(datum) AS max_date FROM az_manager WHERE maID IN(:userIds) GROUP BY maID) azm2 ON azm1.maID = azm2.maID AND azm1.datum = azm2.max_date WHERE azm1.maID IN (:userIds)")
List<TnsWorkingHoursManager> getLastEntries(#Param("userIds") ArrayList<Integer> userIds);
At the second select it says "'SELECT' unexpected"
For anyone else that might stumble upon this question:
If you don't add the nativeQuery = true parameter to the #Query annotation in a Spring Repository, the query will be considered as written in JPQL.
From the JPQL docs:
Subqueries may be used in the WHERE or HAVING clause.
Based on the quote above, the JPQL (Java Persistence Query Language) does not support subqueries in the FROM clause and that is why OP had to make the query native in order for it to work.
I have found a solution.
I forgot to add ", nativeQuery = true" at the end of the line, but in the bracket. Now it works.
I have following model:
#Entity
#Table(name = "SAMPLE_TABLE")
#Audited
public class SampleModel implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#Column(name = "NAME", nullable = false)
#NotEmpty
private String name;
#Column(name = "SHORT_NAME", nullable = true)
private String shortName;
#ManyToOne(fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "MENTOR_ID")
private User mentor;
//other fields here
//omitted getters/setters
}
Now I would like to query only columns: id, name, shortName and mentor which referes to User entity (not complete entity, because it has many other properties and I would like to have best performance).
When I write query:
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<SampleModel> query = builder.createQuery(SampleModel.class);
Root<SampleModel> root = query.from(SampleModel.class);
query.select(root).distinct(true);
root.fetch(SampleModel_.mentor, JoinType.LEFT);
query.multiselect(root.get(SampleModel_.id), root.get(SampleModel_.name), root.get(SampleModel_.shortName), root.get(SampleModel_.mentor));
query.orderBy(builder.asc(root.get(SampleModel_.name)));
TypedQuery<SampleModel> allQuery = em.createQuery(query);
return allQuery.getResultList();
I have following exception:
Caused by: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=generatedAlias1,role=com.sample.SampleModel.model.SampleModel.mentor,tableName=USER_,tableAlias=user1_,origin=SampleModel SampleModel0_,columns={SampleModel0_.MENTOR_ID ,className=com.sample.credential.model.User}}]
at org.hibernate.hql.internal.ast.tree.SelectClause.initializeExplicitSelectClause(SelectClause.java:214)
at org.hibernate.hql.internal.ast.HqlSqlWalker.useSelectClause(HqlSqlWalker.java:991)
at org.hibernate.hql.internal.ast.HqlSqlWalker.processQuery(HqlSqlWalker.java:759)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:675)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:311)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:259)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:262)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:190)
... 138 more
Query before exception:
SELECT DISTINCT NEW com.sample.SampleModel.model.SampleModel(generatedAlias0.id, generatedAlias0.name, generatedAlias0.shortName, generatedAlias0.mentor)
FROM com.sample.SampleModel.model.SampleModel AS generatedAlias0
LEFT JOIN FETCH generatedAlias0.mentor AS generatedAlias1
ORDER BY generatedAlias0.name ASC
I know that I can replace fetch with join but then I will have N+1 problem. Also I do not have back reference from User to SampleModel and I do not want to have..
I ran into this same issue, and found that I was able to work around it by using:
CriteriaQuery<Tuple> crit = builder.createTupleQuery();
instead of
CriteriaQuery<X> crit = builder.createQuery(X.class);
A little extra work has to be done to produce the end result, e.g. in your case:
return allQuery.getResultList().stream()
map(tuple -> {
return new SampleModel(tuple.get(0, ...), ...));
})
.collect(toList());
It's been a long time since the question was asked. But I wish some other guys would benefit from my solution:
The trick is to use subquery.
Let's assume you have Applicant in your Application entity (one-to-one):
#Entity
public class Application {
private long id;
private Date date;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "some_id")
private Applicant applicant;
// Other fields
public Application() {}
public Application(long id, Date date, Applicant applicant) {
// Setters
}
}
//...............
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Application> cbQuery = cb.createQuery(Application.class);
Root<Application> root = cbQuery.from(Application.class);
Subquery<Applicant> subquery = cbQuery.subquery(Applicant.class);
Root subRoot = subquery.from(Applicant.class);
subquery.select(subRoot).where(cb.equal(root.get("applicant"), subRoot));
cbQuery.multiselect(root.get("id"), root.get("date"), subquery.getSelection());
This code will generate a select statement for Application, and select statements for Applicant per each Application.
Note that you have to define an appropriate constructor corresponding to your multiselect.
I got the same problem using EclipseLink as the JPA provider : I just wanted to return the id of a mapped entity («User» in Gazeciarz's example).
This can be achieved quite simply by replacing (in the query.multiselect clause)
root.get(SampleModel_.mentor)
with something like
root.get(SampleModel_.mentor).get(User_.id)
Then, instead of returning all the fields of User, the request will only return the its id.
I also used a tuple query but, in my case, it was because my query was returning fileds from more than one entity.
I thought I know how to use JOIN in JPQL but apparently not. Can anyone help me?
select b.fname, b.lname from Users b JOIN Groups c where c.groupName = :groupName
This give me Exception
org.eclipse.persistence.exceptions.JPQLException
Exception Description: Syntax error parsing the query
Internal Exception: org.eclipse.persistence.internal.libraries.antlr.runtime.EarlyExitException
Users have a OneToMany relationship with Groups.
Users.java
#Entity
public class Users implements Serializable{
#OneToMany(mappedBy="user", cascade=CascadeType.ALL)
List<Groups> groups = null;
}
Groups.java
#Entity
public class Groups implements Serializable {
#ManyToOne
#JoinColumn(name="USERID")
private Users user;
}
My second question is let say this query return a unique result, then if I do
String temp = (String) em.createNamedQuery("***")
.setParameter("groupName", groupName)
.getSingleResult();
*** represent the query name above. So does fname and lname concatenated together inside temp or I get a List<String> back?
Join on one-to-many relation in JPQL looks as follows:
select b.fname, b.lname from Users b JOIN b.groups c where c.groupName = :groupName
When several properties are specified in select clause, result is returned as Object[]:
Object[] temp = (Object[]) em.createNamedQuery("...")
.setParameter("groupName", groupName)
.getSingleResult();
String fname = (String) temp[0];
String lname = (String) temp[1];
By the way, why your entities are named in plural form, it's confusing. If you want to have table names in plural, you may use #Table to specify the table name for the entity explicitly, so it doesn't interfere with reserved words:
#Entity #Table(name = "Users")
public class User implements Serializable { ... }