In my java spring mvc application, i have two classes:
#Entity
public class Transactions {
#Id
#GeneratedValue
long id;
#OneToOne
Customer customer;
#OneToOne
Merchant merchant;
double value;
}
and the customer class is:
#Entity
public class Customer {
#Id
#GeneratedValue
long id;
String name;
String ageClass;
String language;
String cardUserArea;
}
The repository for the Transactions is:
#Repository
public interface TransactionRepository extends JpaRepository<Transactions ,Long >{
#Query("SELECT sum( value) FROM Transactions inner join Customer on Transactions.customer_id=Customer.id where merchant_id= ?1 and age_class= ?2 ")
public double getOverAllValue(String merchantID,String ageGroup);
}
But it complains with:
org.hibernate.hql.internal.ast.QuerySyntaxException: Path expected for join! [SELECT sum( value) FROM ee.seb.domain.Transactions inner join Customer on Transactions.customer_id=Customer.id where merchant_id= ?1 and age_class= ?2 ]
The query runns successfully against the database
The answer was in using nativeQuery
#Query(value = "SELECT sum( value) FROM Transactions inner join Customer on Transactions.customer_id=Customer.id where merchant_id= ?1 and age_class= ?2 ", nativeQuery=true)
public double getOverAllValue(String merchantID,String ageGroup);
Below code is ex. of entity and relationship with other entity :-
#Entity
#Table(name = "F_AREA")
#NamedQuery(name = "FArea.findAll", query = "SELECT f FROM FArea f")
public class FArea implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name = "F_AREA_AREAID_GENERATOR", sequenceName = "SEQ_F_AREA")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "F_AREA_AREAID_GENERATOR")
#Column(name = "AREA_ID")
private long areaId;
#Column(name = "AREA_NAME_EN")
private String areaNameEn;
#Column(name = "AREA_STATUS")
private BigDecimal areaStatus;
#Column(name = "AREA_UPDATED_DATE")
private Timestamp areaUpdatedDate;
// bi-directional many-to-one association to FCity
#ManyToOne
#JoinColumn(name = "CITY_ID")
private FCity FCity;
// bi-directional many-to-one association to FCustomer
#OneToMany(mappedBy = "FArea")
private List<FCustomer> FCustomers;
.
.
.
.
}
Please update your query with this:
#Query("select sum(t.value) from Transactions t inner join t.customer c where c.id= ?1 and c.ageClass= ?2 ")
public Double getOverAllValue(long merchantID,String ageGroup);
Related
I'm using Spring boot 2.3.12.RELEASE and Hibernate 5.4.32.Final. I'm using JOINED inheritance strategy for several classes with parent entity mapped like this:
#Entity
#Table(name = "dos_t_requests")
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Request {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_requests")
#SequenceGenerator(name = "sq_requests", sequenceName = "dos_sq_requests", allocationSize = 1)
private long id;
#Column(name = "req_num")
private String number;
#Column(name = "created_at")
private LocalDateTime createdAt;
#Column(name = "edited_at")
private LocalDateTime editedAt;
#Column(name = "executed_at")
private LocalDateTime executedAt;
#Column(name = "issued_at")
private LocalDateTime issuedAt;
#Lob
#Column(name = "message")
private String message;
...
}
And here is one of child entities:
#Entity
#Table(name = "dos_t_stud_reqs")
#PrimaryKeyJoinColumn(name = "id")
public class StudentRequest extends Request {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "stud_id")
private Student student;
...
}
I need to count the total number of rows of child entities to paginate the results. So I have a method that does all the work:
public Page<RequestPartInfo> getAll(DbUser dbUser, int page, int pageSize) {
...
String cntQuery;
String entQuery;
if (dbUser.getDbType() == DbUserType.STUDENT) {
cntQuery = String.join("\n",
"select count(r.id)",
"from StudentRequest r",
"where r.student.id = :userId");
entQuery = String.join("\n",
"from StudentRequest r",
"join fetch r.document d",
"join fetch d.recipient rc",
"join fetch r.curator c",
"join fetch r.executor e",
"join fetch r.issueType it",
"join fetch r.status st",
"where r.student.id = :userId",
"order by r.createdAt desc");
}
...
long count = em.createQuery(cntQuery, Long.class).setParameter("userId", dbUser.getId()).getSingleResult();
List<RequestPartInfo> requests = em.createQuery(entQuery, Request.class).setParameter("userId", dbUser.getId())
.setFirstResult(page * pageSize).setMaxResults(pageSize).getResultList()
.stream().map(this::mapPartInfo).collect(Collectors.toList());
return PagingUtil.createPage(requests, page, pageSize, count);
}
When I call this method, Hibernate generates an incorrect database query:
select count(studentreq0_.id) as col_0_0_ from oksana.dos_t_stud_reqs studentreq0_ where studentreq0_.id=studentreq0_1_.id and studentreq0_.stud_id=?
The problem with this request is that the WHERE section uses an alias that is not present in the FROM section. So I get following error: java.sql.SQLSyntaxErrorException: ORA-00904: "STUDENTREQ0_1_"."ID": invalid identifier.
I tried to search for a solution to the problem on the internet and found a similar problem here. But it says that this bug has been fixed in Hibernate 5.4.19.
Have 3 entities
Group
#Entity
#Table(name = "`group`")
public class Group implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_group")
private Long id;
#OneToMany(mappedBy = "group",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#OrderColumn(name = "id_student")
private List<Student> students = new ArrayList<>();
#ManyToOne
#JoinColumn(name = "id_faculty")
private Faculty faculty;
.....getters/setters
}
Student
#Entity
#Table(name = "student")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = Long.class)
public class Student implements Serializable {
#Id
#Column(name = "id_student")
private Long id;
......
#OneToMany(mappedBy = "student",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private List<Rating> ratings = new ArrayList<>();
#ManyToOne
#JoinColumn(name = "id_okr")
private OKR okr;
#ManyToOne
#JoinColumn(name = "id_group")
private Group group;
.....getters/setters
}
Rating
#Entity
#Table(name = "rating")
public class Rating implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_rating")
private Long id;
#Temporal(TemporalType.DATE)
#Column
private Date date;
#ManyToOne
#JoinColumn(name = "id_student")
private Student student;
#ManyToOne
#JoinColumn(name = "id_paragraph")
private Paragraph paragraph;
.....getters/setters
}
JPA Query
#Query(value = "SELECT g FROM Group g INNER JOIN FETCH g.students s LEFT JOIN FETCH s.ratings r WHERE g.id = :id AND s.group.id = g.id AND (r.student.id = s.id AND r.date BETWEEN :startMonth and :endMonth OR r IS NULL) GROUP BY s.id")
Group findGroupByStudentGroupId(#Param("id") Long id ,#Param("startMonth") Date startMonth, #Param("endMonth") Date endMonth );
I have the student with id 8000 and after extracting query the result list contains 8001 elements which contain 8 students that I have and 7993 null values. If I remove annotation #OrderColumn I have MultiBagException(cannot simultaneously fetch). If add #OrderColumn to rating #OneToMany association in the entity Student I have null values in Rating collection and in Student collection.
For me, the logic of #OrderColumn which return null values as biggest id in collection seems very strange. Is any way how to solve it?
Hibernate version 5.1.0 Final
Spring Data JPA 1.8.2
As you are trying to fetch students with particular group and ratings, you can have your query fetch from student entity and have join on Ratings entity. You don't have to explicitly add join for group entity as it is already in many to one mapping with student.
You can Change your query as follows:
#Query(value = "SELECT s FROM Student s LEFT JOIN s.ratings r WHERE s.group.id = :id AND (r.date BETWEEN :startMonth and :endMonth OR r IS NULL) GROUP BY s.id")
List<Student> findGroupByStudentGroupId(#Param("id") Long id ,#Param("startMonth") Date startMonth, #Param("endMonth") Date endMonth );
My entity of product looks like below:
#Entity
#Table(name = "order")
public class OrderEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "order_id")
private Long id;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(
name = "order_products",
joinColumns = #JoinColumn(name = "order_id", referencedColumnName = "order_id"),
inverseJoinColumns = #JoinColumn(name = "product_id", referencedColumnName = "id")
)
private Set<ProductEntity> products = new HashSet<>();
}
ProductEntity:
#Entity
#Table(name = "product")
public class ProductEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(unique = true)
private String name;
#ManyToMany(mappedBy = "products")
private Set<OrderEntity> orders = new HashSet<>();
}
I want to get all orders where product name is equal to wanted value. And I write sql query to get result from database, but I cannot write hibernate query for Spring Data JPA.
My query for postgreSQL looks like this:
SELECT o.order_id, op.product_id, p.name
FROM public.order o
INNER JOIN public.order_products op
ON p.order_id = op.product_id
INNER JOIN public.product p
ON op.product_id = p.id
WHERE p.name = 'Foo';
And this query return me an id of order, product_id and name of product. And this works. But I didn't know how to write this question as spring query using #Query.
I need a metohod in my repository:
#Repository
public interface OrderRepository extends JpaRepository<OrderEntity, Long> {
#Query("") <- place for my query in Hibernate sql
List<OrderEntity> findAllByProductName(#Param("name") String name);
}
try this: (it returns full OrderEntity objects )
#Query("select o from OrderEntity o join o.products prod where prod.name = :name")
List<OrderEntity> findAllByProductName(#Param("name") String name);
if you need fetch eager all data for products use : ....OrderEntity o join o.products... in query instead of OrderEntity o join o.products
This is a projection consisting of columns from many entties, so you would have to go for the Result Class strategy.
Basically, you create a POJO class with expected result fields an an equivalent constructor:
public class ResultClass{
private Integer orderId;
private Integer productId;
private String name;
public ResultClass(Integer orderId, Integer productId, String name){
// set the fields
}
}
Then you alter the query a bit:
SELECT new com.mypkg.ResultClass(o.order_id, op.product_id, p.name)
FROM public.order o
INNER JOIN public.order_products op
ON p.order_id = op.product_id
INNER JOIN public.product p
ON op.product_id = p.id
WHERE p.name = 'Foo';
And change the return type on the interface method:
#Repository
public interface OrderRepository extends JpaRepository<OrderEntity, Long> {
#Query("...")
List<ResultClass> findAllByProductName(#Param("name") String name);
}
I have an SQL query that gets me exactly the data I need. The problem is that we are trying to express all queries in JPA Criteria API to maintain portability, and I can't figure out how to map this particular query.
The problem is that the JPA Criteria API Subquery class lacks the multiselect() method that CriteriaQuery class has. As you can see in the SQL query, I have computed fields in the sub-query which don't exist in the entity. Thus, I have no way to retrieve these fields.
I would be quite appreciative if anyone knows a solution or could offer guidance, or even if someone could validate that what I am trying to achieve in JPA Criteria API is not possible.
The SQL:
SELECT w.NAME AS 'wave_name',
Count(*) AS 'num_lines',
Sum(qty_ordered) AS 'num_units',
Count(DISTINCT unit_of_work_id) AS 'num_units_of_work',
Sum(completed_units) AS 'completed_units',
( Sum(completed_units) + Sum(qty_scratched) ) / Sum(qty_ordered) AS 'perc_completed_units'
FROM (SELECT t.id,
t.wave_id,
t.quantity_requested AS 'qty_ordered',
t.quantity_scratched AS 'qty_scratched',
t.unit_of_work_id AS 'unit_of_work_id',
Ifnull(m.quantity, 0) AS 'qty_picked',
CASE
WHEN Ifnull(m.quantity, 0) > quantity_requested THEN
quantity_requested
ELSE Ifnull(m.quantity, 0)
END AS 'completed_units'
FROM task t
LEFT OUTER JOIN (SELECT move.task_id,
Sum(quantity) AS 'quantity'
FROM move
GROUP BY task_id) m
ON m.task_id = t.id) s
JOIN wave w
ON w.id = s.wave_id
GROUP BY w.name;
The entities:
#Entity
#Table(name = "task")
public class Task {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private Long id;
#ManyToOne (cascade = CascadeType.ALL)
#JoinColumn (name = "wave_id", nullable = false)
private Wave wave;
#ManyToOne (cascade = CascadeType.ALL)
#JoinColumn (name = "unit_of_work_id", nullable = false)
private UnitOfWork unitOfWork;
#OneToMany (cascade = CascadeType.ALL, mappedBy = "task")
private Set<Move> moves = new HashSet<Move>();
#Column (name = "quantity_requested")
private Long quantityRequested;
#Column (name = "quantity_scratched")
private Long quantityScratched;
}
#Entity
#Table(name = "wave")
public class Wave {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "wave", cascade = CascadeType.ALL)
private Set<Task> tasks = new HashSet<Task>();
}
#Entity
#Table(name = "unit_of_work")
public class UnitOfWork {
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
#Column (name = "id")
private Long id;
#OneToMany(mappedBy = "unitOfWork", cascade = CascadeType.ALL)
private Set<Task> tasks = new HashSet<Task>();
}
#Entity
#Table(name = "move")
public class Move {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private Long id;
#ManyToOne (cascade = CascadeType.ALL)
#JoinColumn (name = "task_id", nullable = false)
private Task task;
#Column (name = "quantity")
private Long quantity;
}
I would say use named parameters or native query approach for this.
For example:
Named parameters:
public interface Repo extends JpaRepository<AEntity, String> {
#Query("select a from AEntity a where a.BEntity.name = :name")
public aMethod( #Param("name") String name)
}
OR
Native query approach:
public interface Repo extends JpaRepository<AEntity, String> {
#Query(value = "select * from Tablename t where t.name = :name", nativeQuery=true)
public aMethod(#Param("name") String name)
}
Check this link if you are using spring jpa
http://docs.spring.io/spring-data/data-jpa/docs/1.4.x/reference/htmlsingle/#jpa.named-parameters
I have a SQL query
SELECT * FROM Thing AS a JOIN Thing_Property AS b ON a.id=b.Thing_ID
JOIN Property AS c ON b.properties_ID = c.id
JOIN Item AS d ON c.item_ID = d.id
ORDER BY a.name, d.name
and I Eclipselink to create my object model with it.
Here is the model:
#SuppressWarnings("serial")
#Entity
public class Thing implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private int id;
private String name;
#OneToMany(cascade=CascadeType.ALL)
#PrivateOwned
private List<Property> properties = new ArrayList<Property>();
...
// getter and setter following here
}
public class Property implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private int id;
#OneToOne
private Item item;
private String value;
...
// getter and setter following here
}
public class Item implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private int id;
private String name;
....
// getter and setter following here
}
// Code end
but I can not figure out, how to make Eclipselink create the model from that query. Can you help?
You will need to use the API EntityManager.createNativeQuery(String sqlString, Class resultClass);
entityManager.createNativeQuery("SELECT * FROM Thing AS a JOIN Thing_Property AS b ON a.id=b.Thing_ID JOIN Property AS c ON b.properties_ID = c.id JOIN Item AS d ON c.item_ID = d.id ORDER BY a.name, d.name ", Thing.class);