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)
Related
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.
While executing the below code, I got this exception
ERROR org.hibernate.hql.internal.ast.ErrorCounter - Path expected
for join!
Entity class for DB view:
#NamedQueries({
#NamedQuery(name = "find",
query = "SELECT v.cxxx, trk.cxxx, trk.ixxx"
+ "FROM AxxxCxxx as v LEFT JOIN AxxxCxxxTxxx as trk "
+ "ON v.cxxx = trk.cxxx "
)
})
#Entity
#Table(name = "V_XXX")
public class AxxxCxxx implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "CXXX")
private String cxxx;
//getter, setter
}
Entity1 class for DB table:
#Entity
#Table(name = "T_XXX")
public class AxxxCxxxTxxx extends Super implements
Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "CXXX")
private String cxxx;
//getter, setter
}
Super class:
#MappedSuperclass
public abstract class Super implements Serializable {
private static final long serialVersionUID = -1413197336548058176L;
#Id
#Column(name = "TXXX", nullable = false)
private Integer txxx;
#Column(name = "IXXX", length = 15)
private String ixxx;
//getter, setter
}
When I try to execute the NamedQuery, I got this exception:
Caused by: org.hibernate.HibernateException: Errors in named queries: find
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:504)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:444)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:802)
... 26 more
org.hibernate.hql.internal.ast.QuerySyntaxException: Path expected for join! [SELECT v.cxxx, trk.cxxx, trk.ixxx"
+ "FROM AxxxCxxx as v LEFT JOIN AxxxCxxxTxxx as trk "
+ "ON v.cxxx = trk.cxxx]
org.hibernate.hql.internal.ast.InvalidPathException: Invalid path: 'trk.ixxx'
at org.hibernate.hql.internal.ast.util.LiteralProcessor.lookupConstant(LiteralProcessor.java:114) ~[hibernate-core-5.0.5.Final.jar:5.0.5.Final]
Please, can somebody help me? How could I solve the left join with view and table using JPQL for MappedSuperClass?
You need to map the relation between the entities if you want to use JOIN in a JPQL query.
Something like
FROM AxxxCxxx as v INNER JOIN v.axxxCxxxTxxx as trk.
where axxxCxxxTxxx is a field declared in AxxxCxxx class and mapped correctly depending on the DB structure.
Additional info can be obtained here and here
You'll have to use #NamedNativeQuery for SQL queries. With #NamedQuery the query expression is HQL.
Considering the above mapping, you can't really make HQL join, you can use just cartesian product
#NamedQuery(name = "find",
query = "select v.cxxx, trk.cxxx, trk.ixxx "
+ "from AxxxCxxx as v, AxxxCxxxTxxx as trk "
+ "where v.cxxx = trk.cxxx "
)
})
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
I believe that I'm misunderstanding how subselect and eager works; my goal is to improve performance as I encounter the N+1 problem
Edit I'm wondering whether it would be quicker for me to just use the create SQL query method and create the objects myself although I was hoping hibernate would be on par with performance. I can pull back all of the data required, in the example below, in a single query so why on earth is hibernate doing a separate query for each?
I've created the following test case to highlight my issue, please excuse the crudity of this model..
#Entity
#Table(name = "Area")
public class Area implements Serializable
{
#Id
#GeneratedValue(generator = "areaId" )
#GenericGenerator(name = "areaId", strategy = "uuid2")
public String areaId;
#OneToMany(mappedBy = "area", fetch=FetchType.EAGER)
#Fetch(FetchMode.SUBSELECT)
public Set<EmployeeArea> employeeAreas = new HashSet<EmployeeArea>();
}
#Entity
#Table(name = "Employee")
public class Employee implements Serializable
{
#Id
#GeneratedValue(generator = "employeeId" )
#GenericGenerator(name = "employeeId", strategy = "uuid2")
public String employeeId;
#OneToMany(mappedBy = "employee", fetch=FetchType.EAGER)
#Fetch(FetchMode.SUBSELECT)
public Set<EmployeeArea> employeeAreas = new HashSet<EmployeeArea>();
}
#Entity
#Table(name = "EmployeeArea")
public class EmployeeArea implements Serializable
{
#Id
#GeneratedValue(generator = "employeeAreaId" )
#GenericGenerator(name = "employeeAreaId", strategy = "uuid2")
public String employeeAreaId;
#Id
#ManyToOne
public Employee employee;
#Id
#ManyToOne
public Area area;
}
I have then populated with some sample test data: -
Employee employee = new Employee();
Area area = new Area();
EmployeeArea employeeArea = new EmployeeArea();
employeeArea.area = area;
employeeArea.employee = employee;
session.save(employee);
session.save(area);
session.save(employeeArea);
This can be run a few times to provide some data.
I then perform the following: -
session.createQuery("FROM Employee e INNER JOIN e.employeeAreas ea INNER JOIN ea.area").list();
The reason I do the JOIN is so that I can perform specialist searches. I was looking at criteria but it seemed that it didn't allow me to do all that I could with WHERE
I would expect that it would be doing at most 3 queries and 2 sub queries.
SELECT * FROM Employee INNER JOIN EmployeeArea ON condition INNER JOIN Area ON condition
SELECT * FROM Employee WHERE employeeId IN (subquery 1)
SELECT * FROM Area WHERE areaId IN (subquery 2)
In fact, for 6 inputs of the test data aforementioned, I seem to be getting 6 selects for an employee, 6 selects for an area, something that looks like my assumed query for '1.' and then two larger queries that seem just plain wrong: -
select
employeear0_.employee_employeeId as employee2_3_2_,
employeear0_.employeeAreaId as employee1_4_2_,
employeear0_.employee_employeeId as employee2_4_2_,
employeear0_.area_areaId as area3_4_2_,
employeear0_.employeeAreaId as employee1_4_1_,
employeear0_.employee_employeeId as employee2_4_1_,
employeear0_.area_areaId as area3_4_1_,
area1_.areaId as areaId1_0_0_
from
EmployeeArea employeear0_
inner join
Area area1_
on employeear0_.area_areaId=area1_.areaId
where
employeear0_.employee_employeeId in (
select
employee1_.employeeId
from
EmployeeArea employeear0_
inner join
Employee employee1_
on employeear0_.employee_employeeId=employee1_.employeeId
where
employeear0_.area_areaId in (
select
area2_.areaId
from
Employee employee0_
inner join
EmployeeArea employeear1_
on employee0_.employeeId=employeear1_.employee_employeeId
inner join
Area area2_
on employeear1_.area_areaId=area2_.areaId
)
)
then a very similar one for area.
My goal is to be able to use each employee object in the returned list to identify the areas worked in. There would be more fields in each entity however this test case has been simplified.
I solved the problem; it was an issue with my join table. See the following: -
#Id
#ManyToOne
public Employee employee;
#Id
#ManyToOne
public Area area;
I had used #Id which was resulting in the StackOverflowError exception that was being thrown. Using the following query, with a OneToMany fetch of EAGER and #Fetch JOIN on Employee and a OneToMany fetch of LAZY and #Fetch SELECT on Area, I can then perform the following query: -
List<Employee> employees = session.createQuery("FROM Employee e INNER JOIN FETCH e.employeeAreas ea INNER JOIN FETCH ea.area").list();
Whilst being able to use WHERE on the one of the join table columns.
Use JOIN strategy and link Area to EmployeeArea lazily, while Employee eagerly loads EmployeeAreas. When Employee loads EmployeeArea, hibernate session is populated with EmployeeArea objects. Then if you navigate through Employee.EmployeeArea.Area.EmloyeeArea nothing will be get from a database, because we already have EmployeeArea in the session cache.
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 { ... }