( Solved ) How to join "#joinTable" using QueryDSL - jpa

I want to get the SQL to join Summoner, summoner_match and match using JPASQLQuery class.
like this.
SELECT
*, row_number()OVER()
FROM
summoner s
LEFT OUTER JOIN
summoner_match sm
on
s.account_id= sm.account_id
LEFT OUTER JOIN
match m
on
sm.game_id = m.game_id
I add my source code below.
package gg.om.omgg.domain.summoner;
import gg.om.omgg.domain.match.Match;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
#Getter
#NoArgsConstructor
#Entity
public class Summoner implements Serializable {
#Id#Column(length=56)
private String id;
#Column(name="account_id", length=56)
private String accountId;
private int profileIconId;
private long revisionDate;
private String name;
#Column(length=78)
private String puuid;
private long summonerLevel;
#OneToMany
#JoinTable(name="summoner_match",
joinColumns = #JoinColumn(name="account_id"),
inverseJoinColumns = #JoinColumn(name="game_id")
)
private List<Match> matches = new ArrayList<>();
//...
}
package gg.om.omgg.domain.match;
import gg.om.omgg.domain.participant.Participant;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
#Getter
#NoArgsConstructor
#Entity
public class Match {
#Id
#Column(name="game_id")
private long gameId;
private int queueId;
private String platformId;
private int seasonId;
private long gameCreation;
private long gameDuration;
#OneToMany
#JoinColumn(name = "game_id", referencedColumnName = "game_id")
List<Participant> participants = new ArrayList<>();
//...
}
.
.
.
Seems to have found a way.
I will be an advisor.
package gg.om.omgg.domain.summoner;
import com.querydsl.core.Tuple;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.querydsl.jpa.sql.JPASQLQuery;
import com.querydsl.sql.SQLExpressions;
import gg.om.omgg.domain.match.QMatch;
import gg.om.omgg.domain.participant.QParticipant;
import gg.om.omgg.web.dto.SummonerIntegrationInformationResponseDTO;
import lombok.RequiredArgsConstructor;
#RequiredArgsConstructor
public class SummonerCustomRepositoryImpl implements SummonerCustomRepository {
// JPASQLQuery class is registered as #Bean in advance
private final JPASQLQuery jpasqlQuery;
#Override
public void mySolution() {
QSummoner summoner = QSummoner.summoner;
QMatch match = QMatch.match;
// Create Qclass for #JoinTable
QSummoner summoner_match1 = new QSummoner("summoner_match");
QMatch summoner_match2 = new QMatch("summoner_match");
jpasqlQuery
.select(summoner, match, SQLExpressions.rowNumber().over())
.from(summoner)
.leftJoin(summoner_match1)
.on(summoner.accountId.eq(summoner_match1.accountId))
.leftJoin(match).on(summoner_match2.gameId.eq(match.gameId))
.fetch();
}
}

Related

Auto generated String type #Id in Spring Boot to connect mongo

I am incrementing my Autogenerated Id value but I need String type
.This is my service class:
package com.stackroute.orderhistory.service;
import com.stackroute.orderhistory.model.AutogenerateId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import java.util.Objects;
import static org.springframework.data.mongodb.core.FindAndModifyOptions.options;
#Service
public class SequenceGeneratorService {
#Autowired
private MongoOperations mongoOperations;
public String getSequenceNumber(String sequenceName){
Query query=new Query(Criteria.where("order_id").is(sequenceName));
Update update=new Update().inc("seq",1);
AutogenerateId counter = mongoOperations
.findAndModify(query,
update, options().returnNew(true).upsert(true),
AutogenerateId.class);
return !Objects.isNull(counter) ? counter.getSeq() : "1";
}
}
.This is my Entity Class :
import lombok.*;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.mapping.Document;
#NoArgsConstructor
#ToString
#AllArgsConstructor
#Getter
#Setter
#Document(collection = "orders")
public class OrderHistoryModel {
#Transient
public static final String SEQUENCE_NAME="Ordersequence";
#Id
private String orderId;
private String productName;
private int orderAmount;
private String orderPlaced;
private String orderStatus;
private String paymentMethod;
private String shippingAddress;
}
.This is my AutoGenerated Entity class :
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
#Document(collection = "db_sequence")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class AutogenerateId {
#Id
private String id;
private String seq;
}
In this Code I am getting Order_Id as 1,2,3 and so on (In Postman)
But I need Order_Id like order1, order2, order3 and so on...
Please help me out.
In my case, I didn't customise the auto-generation of the id, instead:
in my entity i just annotated the id by #Id s follow:
the DTO:
and here's my service :
the method dtoToUser:
and mongodb will generate a random and unique UUID for me, as follow:
So, Use IDs provided by MongoDB, which are more or less universally unique

java.util.ArrayList cannot be cast to class [Ljava.lang.Object; <springboot test>

I'm currently testing my JPA #Query and keep getting this error
<br>
class java.util.ArrayList cannot be cast to class [Ljava.lang.Object; (java.util.ArrayList and [Ljava.lang.Object; are in module java.base of loader 'bootstrap')
<br>
Member Entity
package org.morgorithm.frames.entity;
import com.sun.istack.Nullable;
import lombok.*;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
#Builder
#AllArgsConstructor
#NoArgsConstructor
#Getter
#ToString
public class Member {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long mno;
private String name;
private String phone;
}
Status Entity
package org.morgorithm.frames.entity;
import com.sun.istack.Nullable;
import lombok.*;
import javax.persistence.*;
#Entity
#Builder
#AllArgsConstructor
#NoArgsConstructor
#Getter
#ToString(exclude={"member","facility"})
public class Status extends BaseEntity{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long statusnum;
#ManyToOne(fetch=FetchType.LAZY)
private Member member;
#ManyToOne(fetch=FetchType.LAZY)
private Facility facility;
private double temperature;
private Boolean state;
}
MemberRepository
package org.morgorithm.frames.repository;
import org.morgorithm.frames.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface MemberRepository extends JpaRepository<Member,Long> {
#Query("select m, s from Member m left join Status s ON s.member=m WHERE m.mno=:mno")
List<Object[]> getMemberWithStatus(#Param("mno") Long mno);
}
what I want to test is that #Query part.
so I made a test file as below
MemberRepositoryTests
package org.morgorithm.frames.repository;
import org.junit.jupiter.api.Test;
import org.morgorithm.frames.entity.Member;
import org.morgorithm.frames.entity.MemberImage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Commit;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.UUID;
import java.lang.Object;
import java.util.stream.IntStream;
#SpringBootTest
public class MemberRepositoryTests {
#Autowired
private MemberRepository memberRepository;
#Autowired
private MemberImageRepository memberImageRepository;
#Test
public void testGetMemberByMno(){
Object result=memberRepository.getMemberWithStatus(100L);
Object[] arr=(Object[])result;
System.out.println("----------------------------*************************");
System.out.println(Arrays.toString(arr));
System.out.println("===================================");
}
}
I want to test this getMemberWithStatus method
but getting those error above..
I think the problem is
Object[] arr=(Object[])result; this part
what did I do wrong??
oh and for your information I did put some test data into DB
solve the problem by changing my code as below.
#Test
public void testGetMemberByMno(){
List<Object[]> result=memberRepository.getMemberWithStatus(100L);
List<Object> arr=Arrays.asList(result);
for(Object[] a:result){
System.out.println(Arrays.toString(a));
}
}

How to use the attribute(column) inherited from parent entity in JPQL when using Single Table Strategy for ORM/JPA

I have three class: User, Admin, NormalUser
Admin and NormalUser extends User Class and I use Single Table Strategy for inheritance when mapped to derby db table and I found out there will be error if I write a named query using inherited attribute in NormalUser class. More information can be referred from below code:
package entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.MappedSuperclass;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="user_type", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorValue("Null")
#Table(name="ALLUSER")
#NamedQueries({
#NamedQuery(name = "User.findAll", query = "SELECT u FROM User u"),
#NamedQuery(name = "User.findByAccount", query = "SELECT u FROM User u WHERE u.account = :account")
})
public class User implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String account;
private String password;
private String userType;
public User() {
super();
}
public User(String account, String password) {
super();
this.account = account;
this.password = password;
}
#Id
#Column(name = "account")
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
#Column(name = "password")
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#Column(name = "user_type", insertable = false, updatable = false, nullable = false)
public String getUserType() {
return userType;
}
public void setUserType(String userType) {
this.userType = userType;
}
#Override
public String toString() {
return account;
}
}
package entity;
import java.io.Serializable;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
#Entity
#DiscriminatorValue("Normal")
#NamedQueries({
#NamedQuery(name = "NormalUser.findAll", query = "SELECT u FROM NormalUser u")
})
public class NormalUser extends User implements Serializable{
/**
*
*/
//private String account;
private static final long serialVersionUID = 1L;
private LinkedHashSet<Customer> customers;
public NormalUser() {
super();
}
#OneToMany(fetch=FetchType.EAGER, mappedBy="user", cascade = {CascadeType.PERSIST, CascadeType.MERGE}) //delete user will delete all customer
public LinkedHashSet<Customer> getCustomers() {
return customers;
}
public void setCustomers(LinkedHashSet<Customer> customers) {
this.customers = customers;
}
// #Column(name = "account")
// //have to override in order to get account to use
// public String getAccount() {
// return account;
// }
//
// public void setAccount(String account) {
// this.account = account;
// }
}
If I write a named query using normal user's inherited attribute:
package entity;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
#Entity
#Table(name="CUSTOMER")
#NamedQueries({
#NamedQuery(name = "Customer.findAll", query = "SELECT c FROM Customer c order by c.customerID desc")
,#NamedQuery(name = "Customer.findByUser", query = "SELECT c FROM Customer c WHERE c.normalUser.account = :account")
})
public class Customer implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private int customerID;
private Industry customerIndustryType;
private String customerName;
private Address customerAddress;
private String customerNationality;
private Date customerAddedDate;
private Double customerDiscountRate;
private String customerScale;
private List<Contact> customerContacts;
private NormalUser normalUser;
//getter and setter omited .....
}
Errors will be thrown when running it and it is apparently related to this query for accessing parent attribute:
Actually, I also try criteria API and it also failed:
// customers = entityManager.createNamedQuery("Customers.findByAccount", Customer.class).setParameter("account", role).getResultList();
// CriteriaBuilder builder = entityManager.getCriteriaBuilder();
// CriteriaQuery<Customer> criteriaQuery = builder.createQuery(Customer.class);
// Root<Customer> c = criteriaQuery.from(Customer.class);
// criteriaQuery.select(c).where(builder.equal(c.get("normalUser").get("account"), role)); //(p.get("price"), budget));//danger4
// Query query = entityManager.createQuery(criteriaQuery);
// //List<Property> properties = query.getResultList();
// customers = query.getResultList();
So How can I use the parent object's attribute? It should not be like this. If I extends the class, I will have all the inherited attributes, isn't it?
---------------------------------------------Update-----------------------------
The inherited attribute can be used in JPQL. It is just that we have to always remember to check the getter name when you annotation on it, which is what is resolved in JPQL.
Hope future visitors remember to pay attention to details.
LOL, The biggest bug comes from the easiest mistake.
The good thing is that due to this error, I have to access customers via user.getCustomers() since it is bidirectional. As a result, I learn a lot to maintain bidirectional relationship.

Spring Data Rest - unable to update nested object with PUT or PATCH

I have Many-To-Many relation between Attribute and AttributeGroup. AttributeGroup contains many attributes and those attributes can be a children of more than one AttributeGroup.
Though Attribute is child of AttributeGroup, it is always created in its own repository, i.e., it is expected to have the Attribute created before AttributeGroup and when the child relationship is added during the creation of AttributeGroup.
Using attribute repo, I could do all the CRUD operations and using attributeGroup repo, successfully process the CRUD operations. During AttributeGroup creation, the relationship to Attribute also work as expected.
Next, if I want to change the relation, i.e., drop an existing Attribute and/or add another another attribute, it does not work.
I read that for updating the nested object PATCH works, but PATCH throws an exception
java.lang.IllegalArgumentException: Can not set java.lang.Long field Attribute.id to AttributeGroup
Please let me know what is wrong with this implementation.
AttributeGroup.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.List;
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
public class AttributeGroup implements Serializable {
private static final long serialVersionUID = -8264102706248686536L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String name;
String displayName;
#NotNull
#Size(min = 1)
#ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE, CascadeType.REFRESH})
List<Attribute> attributes;
}
Attribute.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
public class Attribute implements Serializable {
private static final long serialVersionUID = 8806808817130076030L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String name;
String value;
AttributeType attributeType;
ArrayList<String> values;
#ManyToMany(mappedBy = "attributes")
#ToString.Exclude
List<AttributeGroup> attributeGroups;
}
AttributeTye.java
public enum AttributeType {
TEXT("Text Only");
private final String attributeType;
AttributeType(String productType) {
this.attributeType = productType;
}
}
In bidirectinoal relationships, you need to syncronize both sides of arelationship to be able to delete an entity.
AttributeGroup is the owner of the relationship. So, it must have a method like below in it:
public void removeAttribute(Attribute attribute){
if(attributes.contains(attribute)){
attributes.remove(attribute);
attribute.getAttributeGroups().remove(this);
}
}
Also, in Attribute class you should have:
#PreRemove
void removeAttributeFromAttributeGroups(){
for (AttributeGroup attributeGroup : attributesGroups) {
attributeGroup.removeAttribute(this);
}
}
The same procedure is applied to Add operation.

How to select particular column from different Table using Spring Data JPA Join

I am trying to access particular column from different table using Spring Data JPA Inner Join.
I created four model. AccountModel is main model.
i used manyToOne mapping in AccountModel to the other three model(DepartmentModel,AccountCPCMappingModel,InvestigatorModel). I am trying to access particular column from four table using native Query in AccountRepository
This is my application scenario.
1.AccountModel.java
package com.demo.model;
import java.io.Serializable;
import java.sql.Timestamp;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
#Entity
#Table(name="account")
public class AccountModel implements Serializable {
private static final long serialVersionUID = 1L;
#Id
public Integer naccountid;
public String namount;
public String sacctdesc;
public Integer naccountcpcmappingid;
public Integer nindirectcostrate;
public Integer nagencyid ;
public Integer ndeptid ;
public String sgrantnum;
public Timestamp dstartdate;
public Timestamp denddate;
public String slocation;
public String sclientacctid;
public Integer ninvestigatorid;
public Integer ninstid;
public Integer ntempaccountid;
#ManyToOne(optional = true)
#JoinColumn(name="ndeptid",insertable = false, updatable = false)
public DepartmentModel department;
#ManyToOne(optional = true)
#JoinColumn(name="ninvestigatorid",insertable = false, updatable = false)
public InvestigatorModel investigator;
#ManyToOne(optional = false)
#JoinColumn(name="naccountcpcmappingid",insertable = false, updatable = false)
public AccountCPCMappingModel accountCPC;
//...Getter and Setter
}
2 DepartmentModel
package com.demo.model;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "department")
public class DepartmentModel implements Serializable {
private static final long serialVersionUID = 1L;
#Id
public Integer ndeptid;
public String sdeptname ;
public Integer ninstid ;
public Boolean bislocked;
public String sclientdeptid;
public Integer nsurveymethodid;
public Boolean bisjointuse;
public Integer ntempdeptid;
public Boolean balternatejointusepercentage;
public Integer ndivid;
//...Getter and Setter
3.InvestigatorModel
package com.demo.model;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="investigator")
public class InvestigatorModel implements Serializable{
private static final long serialVersionUID = 1L;
#Id
public Integer ninvestigatorid;
public String sinvestigatorname;
public Integer ninstid ;
public String stitle;
public Integer ntempinvestigatorid;
public Integer nempid;
AccountRepository
package com.demo.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import com.demo.model.AccountModel;
#Repository
public interface AccountRepository extends JpaRepository<AccountModel, Integer>{
#Query(value="select acct.sclientacctid,acct.sacctdesc,acct.slocation,invest.sinvestigatorname, \r\n" +
"dept.sclientdeptid,dept.sdeptname,acp.sccpcode \r\n" +
"from Account acct join Department dept on acct.nDeptID=dept.nDeptID \r\n" +
"join Investigator invest on acct.nInvestigatorID=invest.nInvestigatorID \r\n" +
"join AccountCPCMapping acp on acct.nAccountCPCMappingID=acp.nAccountCPCMappingID \r\n" +
"where acct.nInstID=60 \r\n" +
"order by acct.sclientacctid Asc",nativeQuery=true)
List<Object[]> findByNaccountid();
}
AccountService
package com.demo.services;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.demo.model.AccountModel;
import com.demo.repository.AccountRepository;
#Service
public class AccountService {
#Autowired
AccountRepository accRepo;
public List<AccountModel> findLocation()
{
return accRepo.findBySlocation();
}
public Set<Object[]> gridLoad()
{
Set<Object[]> gridObj=accRepo.findByNaccountid();
return gridObj;
}
}
AccountController
package com.demo.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.demo.model.AccountModel;
import com.demo.services.AccountService;
#RestController
#RequestMapping("/SpaceStudy/SpaceAdmin")
public class AccountController
{
#Autowired
AccountService accService;
#CrossOrigin(origins="*")
#GetMapping("/AccountMaintenance/LoadLocation")
public List<AccountModel> findLocation()
{
return accService.findLocation();
}
#CrossOrigin(origins="*")
#PostMapping("/AccountMaintenance/LoadGrid")
public Set<Object[]> GridLoad()
{
return accService.gridLoad();
}
}
I am getting output in this format
But I want output in JSON Format like this (key,value pairs)
sample output
[
{
"userId": 1,
"id": 1
}
]
Can any one help me what i need to change in my code for json data.
Your query should return List<Object[]> and not List<AccountModel>.
JPA equivalent query will be something like this.
Query("select acct.sclientacctid,acct.sacctdesc,acct.slocation,invest.sinvestigatorname, dept.sclientdeptid,dept.sdeptname,acp.sccpcode
from AccountModel acct join acct.department dept join acct.investigator invest join acct.accountCPC acp
where acct.nInstID= :instId
order by acct.sclientacctid")
List<Object[]> findByInstId (#Param("instId") Integer instId);
This will return you a List of Object array and the array will have the columns in order it appears in the select. Give it a try.