Spring Data JPA find by nested object id (nested twice) - spring

Is it possible?
I'd like to get all AAA objects with a specific CCC.incidentAssessmentResultId id using JPARepository. Is it possible?
#Entity
#Table(schema = "aaa", name = "table")
public class AAA {
#Column(name = "kryterium")
private String criterion;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "id_kryterium_naruszen")
private List<BBB> violationFactors;
}
public class BBB {
#Column(name = "czynnik")
private String factor;
#Column(name = "stopien")
private float degree;
#JsonManagedReference
#OneToOne(mappedBy = "violationFactor")
private CCC incidentAssessmentFactor;
}
public class CCC {
#Column(name="komentarz")
private String comment;
#Column(name="ocena_naruszenia_wynik_id", updatable=false, insertable=false)
private Long incidentAssessmentResultId; //-> I'd like to find AAA objects with a specific incidentAssessmentResultId ID
#Column(name="czynnik_wybrany")
private Boolean factorIsSelected;
#Column(name = "wartosc_wybrana")
private float value;
#Repository
public interface ViolationCriterionRepository extends JpaRepository<AAA, Long> {
// #Query("select vc from AAA vc left join vc.violationFactors vf left join vf.incidentAssessmentFactor iaf where iaf.incidentAssessmentResultId = ?1")
List<AAA> findByViolationFactors_IncidentAssessmentFactor_IncidentAssessmentResultId(Long incidentId);
}
Now, when I call ViolationCriterionRepository .findAll() I get all data but I want to get all data but with certain CCC objects. I've tried with the method below in my Repository but I get 0 results.
UPDATE
My repo:
#Repository
public interface ViolationCriterionRepository extends JpaRepository<ViolationCriterion, Long> {
#Query("select vc from AAA vc join vc.violationFactors vf join vf.incidentAssessmentFactor iaf where iaf.incidentAssessmentResultId = ?1")
List<AAA> findByIncidentAssessmentResultId(Long incidentId);
}

In the AAAJpaRepository:
List<AAA> aaaList = findByViolationFactorsIncidentAssessmentFactorIncidentAssessmentResultId( long incidentAssessmentResultId);

And the answer is (#JB Nizet - many thanks!):
#Repository
public interface ViolationCriterionRepository extends JpaRepository<ViolationCriterion, Long> {
#Query("select vc from AAA vc join vc.violationFactors vf join vf.incidentAssessmentFactor iaf join iaf.incidentAssessment ia where ia.incidentAssessmentId = ?1 group by vc ")
List<ViolationCriterion> findIncidentAssessmentByIncidentAssessmentId(Long incidentId);
}

Related

Spring JPA one to many join

#Entity
#Getter #Setter
public class IndieApp {
#Id
#Column(name = "indie_app_id")
private Long id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "indieApp")
private List<Genre> genres = new ArrayList<>();
}
#Entity
#Getter #Setter
public class Genre {
#Id
#Column(name = "genre_id")
private Long genreId;
#Column(name = "description")
private String description; //like "RPG", "Action"
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "indie_app_id")
private IndieApp indieApp;
}
#Data
public class RandomRecDto {
private final Long id;
private final String name;
private final String genres;
}
#Repository
#RequiredArgsConstructor
public class RandomRecRepository {
private final EntityManager em;
public List<RandomRecDto> findRandomApps() {
return em.createQuery(
"select new study.weba.studyJPA.dto.RandomRecDto(i.id, i.name, g.description)" +
" from IndieApp i join i.genres g", RandomRecDto.class)
.setMaxResults(12)
.getResultList();
}
}
Hello seniors!
When i put the dummy data as shown below,
indie_app_id
name
1
App1
2
App2
genre_id
description
indie_app_id
1
Action
1
2
RPG
1
3
FPS
2
4
Sport
2
i can get result like this.
randomRecDto = RandomRecDto(id=1, name=App1, genres=Action)
randomRecDto = RandomRecDto(id=1, name=App1, genres=RPG)
randomRecDto = RandomRecDto(id=2, name=App2, genres=FPS)
randomRecDto = RandomRecDto(id=2, name=App2, genres=Sport)
However, result that i want is like this.
randomRecDto = RandomRecDto(id=1, name=App1, genres=Action, RPG)
randomRecDto = RandomRecDto(id=2, name=App2, genres=FPS, Sport)
I want to get description by array.
What should I do?
Why don't you get IndieApp entities and then convert them into your RandomRecDto DTO? This would even simplify your Repository that would handle IndieApp instead of RandomRecDto:
public interface IndieAppRepository extends JpaRepository<IndieApp, Long> {
List<IndieApp> findTop12();
}
You may want to read more details about Spring Data in the following links:
Spring Data JPA
Spring Data Repositories
Limiting query results with Spring Data JPA
Defining Spring Data Query Methods
Spring Data Repositories Query Keywords
JpaResultMapper jpaResultMapper = new JpaResultMapper();
String sql = "SELECT i.indie_app_id, i.name, group_concat(g.description separator ',') FROM indie_app AS i" +
" JOIN genre AS g ON i.indie_app_id = g.indie_app_id" +
" group by i.indie_app_id, i.name";
Query nativeQuery = em.createNativeQuery(sql);
List<RandomRecDto> results = jpaResultMapper.list(nativeQuery, RandomRecDto.class);

Query syntax in URL when using Spring and #QuerydslPredicate

How can I write the HTTP request URL in order to get a query similar to:
select *
from incidents i,
jira_issues ji
where i.incident_id = ji.incident_id
and ji.external_jira_issue_id = 'ABC-123'
and ji.jira_server_id = '1'
I have the following classes:
#Entity(name = "incidents")
public class IncidentEntity {
#OneToMany(
mappedBy = "incident",
cascade = CascadeType.ALL
)
#LazyCollection(LazyCollectionOption.FALSE)
private List<JiraIssueEntity> jiraIssues;
...
}
#Entity(name = "jira_issues")
public class JiraIssueEntity {
#EmbeddedId
#EqualsAndHashCode.Include
private JiraIssueId id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "incident_id")
#ToString.Exclude
private IncidentEntity incident;
...
}
#Embeddable
public class JiraIssueId implements Serializable {
#EqualsAndHashCode.Include
private String externalJiraIssueId;
#EqualsAndHashCode.Include
private String jiraServerId;
}
This is my API method signature:
#GetMapping("")
public Page<Incident> listIncidents(
#QuerydslPredicate(root = IncidentEntity.class) Predicate predicate
);
I know that I can send something like:
/incidents/?jiraIssues.id.externalJiraIssueId=ABC-123&jiraIssues.id.jiraServerId=1"
This translates to the following query:
select *
from incidents incidenten0_
where (exists(select 1
from jira_issues jiraissues1_
where incidenten0_.incident_id = jiraissues1_.incident_id
and (lower(jiraissues1_.external_jira_issue_id) like ? escape '!')))
and (exists(select 1
from jira_issues jiraissues2_
where incidenten0_.incident_id = jiraissues2_.incident_id
and (lower(jiraissues2_.jira_server_id) like ? escape '!')))
which is not so good.
I don't know how to:
Do equals and not contains (rows with externalJiraIssueId=ABC-1234 will return as well but I don't want that).
Check that same JiraIssue has externalJiraIssueId=ABC-123 and jiraIssues.id.jiraServerId=1 and not different JiraIssues that each matches one (something like jiraIssues.id=(ABC-123, 1)
Thank you.
regarding the first problem you can make your 'repository' interface extend QuerydslPredicateExecutor and QuerydslBinderCustomizer
then you can override the 'customize' method with something like this:
#Override
default void customize(QuerydslBindings bindings, #NotNull QIncidentEntity root)
{
bindings.bind(String.class)
.first((SingleValueBinding<StringPath, String>)
StringExpression::equalsIgnoreCase);
}
this will make the query check for equals (ignoring the case) and not contains.

JpaRepository - Inner Join - Subsequent selects

I'm facing a problem when I try to get an list of ServiceCup with its ServiceLanguage. When I try to manipulate the list of ServiceCup in my service layer hibernate is executing a second query and populate my ServiceCup with all ServiceLanguage again.
ServiceCup x ServiceLanguage x LanguageCup
ServiceCup:
#Data
#Entity
#Table(name = "csm_service")
public class ServiceCup extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
private String context;
// bi-directional many-to-one association to CsmServiceLanguage
#OneToMany(mappedBy = "service")
private List<ServiceLanguage> serviceLanguages;
}
ServiceLanguage:
#Data
#Entity
#Table(name = "csm_service_language")
public class ServiceLanguage extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "translated_name")
private String translatedName;
// bi-directional many-to-one association to CsmLanguage
#ManyToOne
#JoinColumn(name = "csm_language_id_fk")
private LanguageCup language;
// bi-directional many-to-one association to CsmService
#ManyToOne
#JoinColumn(name = "csm_service_id_fk")
private ServiceCup service;
}
JpaRepository:
#Query(value = "select s, sl from ServiceCup s \n" + "INNER JOIN FETCH ServiceLanguage sl on s.id = sl.service \n"
+ "where sl.language.id = :languageId")
List<ServiceCup> findAllServicesByLanguageId(#Param("languageId") String languageId);
Query in repository layer:
select *all_fields* from csm_service servicecup0_ inner join csm_service_language servicelan1_ on (servicecup0_.id=servicelan1_.csm_service_id_fk) where servicelan1_.csm_language_id_fk=?
But in service layer execute a lot of queries to bring all the relations of ServiceCup. I want the ServiceCup objects populate but only with the results that are in the query.
How can I get a ServiceCup object with only the results of the query?
PS: In my method in service layer I have #Transactional(readOnly = true) but if I remove I can't get the objects related to ServiceCup.
I needed to use projection and not the entity to execute the query only once.
ServiceCupProj
public interface ServiceCupProj {
public Long getId();
public String getDescription();
public String getInternalname();
......
Repository
#Repository
public interface CupServiceRepository extends JpaRepository<ServiceCup, Long> {
#Query(value = "select servicecup0_.id as id, servicelan1_.translated_description as description, servicelan1_.translated_name as internalname \n"
+ "from csm_service servicecup0_ inner join csm_service_language servicelan1_ on (servicecup0_.id=servicelan1_.csm_service_id_fk) \n"
+ "where servicelan1_.csm_language_id_fk = :languageId ", nativeQuery = true)
List<ServiceCupProj> findAllServicesByLanguageId(#Param("languageId") String languageId);
......
And in the service layer I transform this ServiceCupProj in the entity that I need.

Spring boot JPA filter by join table

I cant find something concrete in the docs (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation)
and no satisfying answere in several blogs.
so here my question.
I have table Entity like:
#Entity
#Table(name = "workstation")
public class Workstation
{
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "userid", nullable = false)
public User user;
}
And the user table Entity:
public class user
{
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "ID", unique = true, nullable = false)
public Integer id;
#Column(name = "age", nullable = false)
public int age
}
Now I would like to have a query in my Repository like this:
public interface WorkstationRepo extends CrudRepository<Workstation, Long> {
findByUserAgeLesserThan(int maxAge);
}
Means I want to find all user who are under a certain age through my Workstation Entity.
And is it possible without a #Query annotation? Or/And how should it look like?
Try this
List<Workstation> findByUserAgeLessThan(int maxAge);
Alternatively you can also write your query
#Query("Select w from Workstation w where w.user.age < :maxAge")
List<Workstation> findByUserAgeLesserThan(#Param("maxAge") int maxAge);
This works:
#Query("SELECT w from Workstation w INNER JOIN w.user u where u.age < ?1")
List<Workstation> findByUserAgeLessThan(int age);
You should have something like this:
#Query("SELECT w from Workstation w INNER JOIN w.user u where u.age < :age")
List<Workstation> findByUserAgeLessThan(#Param("age") int age);
Basically, you need to JOIN the tables using JPQL.

How to get data from tables in spring data jpa?

I have two tables
#Entity
#Table(name = "TAX_CATEGORY")
public class TaxCategory {
#Id
#GeneratedValue
#Column(name = "ID", nullable = false)
private long id;
#Column(name = "CATEGORY", nullable = false)
private String category;
#Column(name = "TAX", nullable = false)
private Double tax;
#Entity
#Table(name = "PRODUCT")
public class Product {
#Id
#GeneratedValue
#Column(name = "ID", nullable = false)
private long id;
#Column(name = "PRICE", nullable = false)
private Double price;
#Column(name = "NAME", nullable = false)
private String name;
#OneToOne
#JoinColumn(name = "TAX_CATEGORY_ID")
private TaxCategory taxCategory;
Now I want to query
"Select p.name, p.price, t.tax from Product p, TaxCategory t join p.taxCategory.id=t.id"
So List it would return is
ProductName ProductPrice Tax
but I am not able to get this data from two tables. Single table data is working fine.
public interface CustomRepositoryCustom {
public void customMethod();
}
public interface CustomRepository
extends JpaRepository<Account, Long>, CustomRepositoryCustom { }
public class CustomRepositoryImpl implements CustomRepositoryCustom {
public void customMethod() {
Query nativeQuery = entityManager.createNativeQuery("Select p.name, p.price, t.tax from Product p, TaxCategory t join p.taxCategory.id=t.id");
return query.getResultList();
}
}
This throws exception that object is not managed bean. If I create custom object then also it gives similar type of issues.
Use the following JPA query to get the both tables data. Here used jpa query to fetch the product. From product object, can get the taxCategory.
public interface CustomRepository extends JpaRepository<Account, Long>, CustomRepositoryCustom {
Query("select product from Product as product join fetch product.taxCategory as taxCategory where taxCategory.id = :taxCategoryId")
public Product getProductByCategory(#Param Long taxCategoryId);
}
Instead of query method you can directly define JPA method to find products based on category Id as.
#Repository
#RepositoryRestResource
public interface ICountryRepository extends JpaRepository<Product , Long > {
List<Product> findByTaxCategory_Id(#Param Long Id);
}

Resources