Spring projections select collection - spring-data

I am attempting to have a station projection include a list of associated logos. Below is my domain:
#Table(name = "Station")
public class Station implements Serializable {
#Id
#Column(name = "Id")
private int id;
#OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "station")
private Set<Logo> logos;
}
The #OneToMany associated logos:
#Table(name = "Logo")
public class Logo {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Transient
private String fullUrl; // calculated
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "StationId", nullable = false)
private Station station;
}
My repository and query is as follows:
#Query(value = "SELECT s.id AS Id, s.logos As Logos FROM Station s JOIN s.users su WHERE su.username = ?1")
Collection<StationListViewProjection> findStationsByUsername(String username);
My station projection expects the Id and a list of logoProjections
#Projection(name = "StationListViewProjection", types = Station.class)
public interface StationListViewProjection {
int getId();
Set<LogoProjection> getLogos();
}
The logoProjection only needs the url
#Projection(name = "LogoProjection", types = Logo.class)
public interface LogoProjection {
String getFullUrl();
}
When i execute my query I get a strange response:
MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'as col_5_0_, . as col_6_0_, stationlog3_.id as id1_20_0_'

If I understand this
#Transient
private String fullUrl; // calculated
correct, your fullUrl gets calculated inside your java code and more important, it doesn't have a matching column in the database. You can't use such field in projections directly. You might be able to use an Open Projection and specify the calculation to obtain the fullUrl using a #Value annotation.

Related

JPA Repository Query on additional table #ManytoMany

I want to do select like this in my jpa spring repository
SELECT sicknes_id, count(symptomp_id) as ilosc FROM symptomp_sicknes where symptomp_id IN (1,2) group by sicknes_id Order by ilosc DESC;
My enitity
#Entity
#Table(name = "symptomp")
public class Symptomp {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "symptomp_id")
private Long symptomp_id;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#ManyToMany(cascade = {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH}, fetch = FetchType.LAZY)
#JoinTable(name = "symptomp_sicknes",joinColumns = #JoinColumn(name = "symptomp_id"),inverseJoinColumns = #JoinColumn(name = "sicknes_id"))
private Set<Sicknes> sicknes = new HashSet<>();
#Entity
#Table(name = "sicknes")
public class Sicknes {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sicknes_id")
private Long sicknes_id;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#ManyToOne(cascade = {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH}, fetch = FetchType.LAZY)
#JoinColumn(name = "speciesId")
private Species species;
My Symptomp repository:
public interface SymptompRepository extends JpaRepository<Symptomp, Long> {
#Query("select p from Symptomp p where name like ?1%")
public List<Symptomp> findAllBySymptompName(String symptomp);
public Symptomp findByName(String symptomp);
public List<Symptomp> findByNameIn(List<String> list);
Integer countDistinctSymptompByName(String id);
}
How I can create this select in my JPA repository?
I try get value like in select but i got error mapping bean.
You can get query result as List<Object[]> using nativeQuery=true parameter
#Query("SELECT sicknes_id, count(symptomp_id) as ilosc FROM symptomp_sicknes where symptomp_id IN (1,2) group by sicknes_id Order by ilosc DESC", nativeQuery=true)
List<Object[]> getQueryResult();
Other option is to create dto class with appropriate constructor
public class QueryResultDto {
Long sicknesId;
Long count;
public QueryResultDto(Long sicknesId, Long count) {
this.sicknesId = sicknesId;
this.count = count;
}
}
Then using JPQL
#Query("select new yourproject.dtopath.QueryResultDto(...")
List<QueryResultDto> getQueryResult(#Param("symptompIds") List<Long> symptompIds);
If you want to avoid a native Query the best way is to create an Entity for that JoinTable. Then you can query it easily. Additional benefit if this is that if in future a requirement will pop up that you have to store additional attributes in that relation you will have the Entity already there to do that easily.

#NamedQuery trying to fetch entity and related entity (#OneToOne relation) by related entity property

There are two entities:
#Entity(name = "Account")
#Table(name = "accounts")
#NamedQuery(name = "findAccountByExtId",
query = "SELECT a " +
"FROM Account a " +
"WHERE a.accountDetails.extId = :extId " +
"AND a.deletedAt IS NULL")
public class Account extends DeletableAudit {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private UUID id;
#OneToOne(mappedBy = "parent", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private AccountDetails accountDetails;
and
#Entity(name = "AccountDetails")
#Table(name = "account_details")
public class AccountDetails extends DeletableAudit {
private static final long serialVersionUID = -1L;
#Id
#GeneratedValue
private Integer accDetId;
#OneToOne
#JoinColumn(name = "fk_account")
private Account parent;
#Column
private String extId;
Goal is to return Account and AccountDetials via the external ID. The program won't run due to error:
Failed to create query for method public abstract p.j.o.d.d.Account p.j.o.d.r.AccountRepository.findAccountByExtId(java.lang.String)! No property extId found for type Account!
Already tried different variations of #OneToOne mapping and query, however nothing seems to work.
Second question is whether it is possible to have AccountDetails reference the same ID as Account. I'd like to get rid of accDetId and just store these entities with the same UUID of their parent.

Composite key query in JPQL not generated correctly

I use spring data jpa and I use composite key
#Entity
#IdClass(SamplesPK.class)
public class Samples extends BaseEntity {
#Id
private String sampleLetter;
#Embedded
private TestSamples testSamples;
#Id
#ManyToOne(optional=false)
#JoinColumns({
#JoinColumn(name = "sampling_id", referencedColumnName = "id"),
#JoinColumn(name = "sampling_year", referencedColumnName = "year")})
private Samplings sampling;
//get set
}
public class SamplesPK implements Serializable {
private SamplingsPK sampling;
private String sampleLetter;
public SamplesPK(SamplingsPK sampling, String sampleLetter) {
this.sampling = sampling;
this.sampleLetter = sampleLetter;
}
//get set
}
#Entity
#IdClass(SamplingsPK.class)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Samplings {
#Id
private Integer year;
#Id
#GeneratedValue
private Integer id;
#OneToMany(mappedBy = "sampling", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Samples> samples = new ArrayList<>();
//get set
}
public class SamplingsPK implements Serializable {
private int year;
private Integer id;
public SamplingsPK(int year, Integer id) {
this.id = id;
this.year = year;
}
}
I try to do search sample with composite key
#Query(value = "select s from Samples s Join fetch s.sampling sp Join fetch sp.product p Join fetch p.productType Join Fetch s.testSamples.compressionTest where s.id=:id and s.year=:year and s.sampleLetter=:sampleLetter and sp.id=:id and sp.year=:year")
public Samples findSamplesWithFullProductAndTest(#Param("id") Integer id, #Param("year") Integer year, #Param("sampleLetter") String sampleLetter);
I get this error
java.lang.IllegalArgumentException: Parameter value [2] did not match
expected type [com.lcm.model.SamplesPK (n/a)]
Edit,
I modified query to
select s from Samples s Join fetch s.sampling sp Join fetch sp.product p Join fetch p.productType Join Fetch s.testSamples.compressionTest where s.sampling.id=:id and s.sampling.year=:year and s.sampleLetter=:sampleLetter and sp.id=:id and sp.year=:year
but I get this error
org.postgresql.util.PSQLException: ERROR: operator does not exist:
record = integer Indice : No operator matches the given name and
argument type(s). You might need to add explicit type casts.
Generated query is
select
samples0_.sample_letter as sample_l1_20_0_,
samples0_.sampling_id as sampling0_20_0_,
samplings1_.id as id2_21_1_,
samplings1_.year as year3_21_1_,
products2_.id as id2_15_2_,
producttyp3_.id as id1_16_3_,
compressio4_.id as id1_3_4_,
samples0_.sampling_id as samplin27_20_0_,
samples0_.sampling_year as samplin28_20_0_,
samples0_.compression as compres18_20_0_,
samples0_.compression_number as compres19_20_0_,
samples0_.compression_test_id as compres29_20_0_,
samplings1_.available_for_test as availabl4_21_1_,
samplings1_.dtype as dtype1_21_1_,
products2_.created_at as created_3_15_2_,
products2_.updated_at as updated_4_15_2_,
products2_.name_en as name_en5_15_2_,
products2_.dtype as dtype1_15_2_,
producttyp3_.created_at as created_2_16_3_,
producttyp3_.updated_at as updated_3_16_3_,
producttyp3_.name_en as name_en4_16_3_,
compressio4_.created_at as created_2_3_4_,
compressio4_.updated_at as updated_3_3_4_
from
permacon.samples samples0_
inner join
permacon.samplings samplings1_
on samples0_.sampling_id=samplings1_.id
and samples0_.sampling_year=samplings1_.year
inner join
permacon.products products2_
on samplings1_.product_id=products2_.id
inner join
permacon.product_types producttyp3_
on products2_.product_type_id=producttyp3_.id
inner join
permacon.compressions compressio4_
on samples0_.compression_test_id=compressio4_.id
where
(
samples0_.sampling_id, samples0_.sampling_year
)=?
and samplings1_.year=?
and samples0_.sample_letter=?
and samplings1_.id=?
and samplings1_.year=?
Edit 2
#MappedSuperclass
public abstract class BaseEntity {
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
//get set...
}
Inheritance is used because I have two table who extends Samplings
you year field is a int put int instead of Integer in your
findSamplesWithFullProductAndTest
method

How to use JPA criteriaBuilder to search on attributes in a collection of sub-attributes

I have an Entity that maps to a table defined this way:
#Entity
#Table(name = "cmmn_calendar_evnt")
public class CommonCalendarEvent implements java.io.Serializable
{
private Integer cevId;
private Set<CommonCalendarEventPart> commonCalendarEventParts = new HashSet<CommonCalendarEventPart>(0)
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "CEV_ID", unique = true, nullable = false)
public Integer getCevId()
{
return this.cevId;
}
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "commonCalendarEvent")
public Set<CommonCalendarEventPart> getCommonCalendarEventParts()
{
return this.commonCalendarEventParts;
}
}
and CommonCalendarEventPart is defined like this:
#Entity
#Table(name = "cmmn_calendar_evnt_part")
public class CommonCalendarEventPart implements java.io.Serializable
{
private static final long serialVersionUID = 1L;
private Integer ceeId;
private CommonCalendarEvent commonCalendarEvent;
private PartParticipant partParticipant;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "CEE_ID", unique = true, nullable = false)
public Integer getCeeId()
{
return this.ceeId;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "CEE_CEV_ID", nullable = false)
public CommonCalendarEvent getCommonCalendarEvent()
{
return this.commonCalendarEvent;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "CEE_PPT_ID", nullable = false)
public PartParticipant getPartParticipant()
{
return this.partParticipant;
}
}
and finally:
#Entity
#Table(name = "part_participant")
public class PartParticipant implements java.io.Serializable
{
private static final long serialVersionUID = 1L;
private Integer pptId;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "PPT_ID", unique = true, nullable = false)
public Integer getPptId()
{
return this.pptId;
}
}
I want to use the CriteriaBuilder to generate a query finding all CommonCalendarEvent for a specific Participant ID.
In Hql it would look something like this: (although I have not confirmed that this Hql is correct either)
"from commonCalendarEvent cce where :pptId in (cce.commonCalendarEventParts.partParticipant.pptId)"
I've tried some approaches of what I thought were intuitive attempts at writing a criteriaBuilder approach, but my attempts have resulted in errors ranging from:
“unexpected end of subtree” to just implementation errors.
.....
CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<CommonCalendarEvent> criteria = builder.createQuery(CommonCalendarEvent.class);
Root<CommonCalendarEvent> root = criteria.from(CommonCalendarEvent.class);
Fetch<CommonCalendarEvent, CommonCalendarEventPart> evf = root.fetch(CommonCalendarEvent_.commonCalendarEventParts, JoinType.LEFT);
Join<CommonCalendarEvent, CommonCalendarEventPart> evj = (Join<CommonCalendarEvent, CommonCalendarEventPart>) evf;
Join<CommonCalendarEventPart, PartParticipant> evpj = evj.join(CommonCalendarEventPart_.partParticipant);
List<Predicate> pred = new ArrayList<Predicate>();
pred.add(builder.equal(evpj.get(PartParticipant_.pptId), pptId));
criteria.where(builder.and(pred.toArray(new Predicate[] {})));
return getEntityManager().createQuery(criteria).getResultList();
.............
above yields an "unexpected end of subtree" error.
Any Help is appreciated.
+1 for using Lazy initialization. The JPA model is Object, or Entity oriented, so you need to get used to thinking in those terms. A PartParticipant is not identified by its id in JPA, but by the object itself. Assuming you have a list of participants:
PartParticipant pp = em.find(PartParticipant.class, 2);
List<PartParticipant> pps = new ArrayList<PartParticipant>();
pps.add(pp);
Then you pass that list to the queries. In JPQL:
TypedQuery<CommonCalendarEvent> cev = em.createQuery("select cev from CommonCalendarEvent cev join fetch cev.commonCalendarEventParts cce where cce.partParticipant in :pps", CommonCalendarEvent.class);
List<CommonCalendarEvent> cevs = cev.setParameter("pps", pps).getResultList();
Notice the fetch is needed to prevent LazyInitializationExceptions.
Knowing the JPQL, the CriteriaQuery should follow pretty much the same:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<CommonCalendarEvent> q = cb.createQuery(CommonCalendarEvent.class);
Root<CommonCalendarEvent> r = q.from(CommonCalendarEvent.class);
Join<CommonCalendarEvent, CommonCalendarEventPart> j = r.join("commonCalendarEventParts");
r.fetch("commonCalendarEventParts");
q.select(r).where(j.get("partParticipant").in(pps));
List<CommonCalendarEvent> rs = em.createQuery(q).getResultList();
You don't need to do anything special with the fetch other than execute it. As you can see, the query uses the PartParticipant Id.
select
commoncale0_.CEV_ID as CEV_ID1_0_0_,
commoncale1_.CEE_ID as CEE_ID1_1_1_,
commoncale1_.CEE_CEV_ID as CEE_CEV_2_1_1_,
commoncale1_.CEE_PPT_ID as CEE_PPT_3_1_1_,
commoncale1_.CEE_CEV_ID as CEE_CEV_2_0_0__,
commoncale1_.CEE_ID as CEE_ID1_1_0__
from cmmn_calendar_evnt commoncale0_
inner join cmmn_calendar_evnt_part commoncale1_ on commoncale0_.CEV_ID=commoncale1_.CEE_CEV_ID
where commoncale1_.CEE_PPT_ID in (?)
Fetch<CommonCalendarEvent, CommonCalendarEventPart> evf is not necessary, and the first join statement should be corrected:
Join<CommonCalendarEvent, CommonCalendarEventPart> evj =
root.join(CommonCalendarEvent_.commonCalendarEventParts);
The rest of the query seems correct.

QueryDSL / JPQL : how to build a join query?

I've tried to read through the QueryDSL docs but I am still very confused. I'm accustomed to writing a lot of SQL, but this is my first real crack at using QueryDSL w/ JPQL (JPA2).
I have the following entity:
#Entity
public class Provider implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Version
#Column(name = "version")
private Integer version;
private String name;
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(name = "provider_contact", joinColumns = #JoinColumn(name = "contact_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "provider_id", referencedColumnName = "id"))
#OrderColumn
private Collection<Contact> contact;
}
where Contact is a simple entity with an id for a pk.
#Entity
public class Contact {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
/**
* User first name
*/
#NotNull
private String firstName;
/**
* User last name
*/
#NotNull
private String lastName;
}
I'm trying to write a query which returns a Contact object given a specific Contact.id and Provider.id. If the Contact object is not a part of the Provider's Contact collection, I'm looking for a null value.
I've tried the following:
public Contact getContact( long providerId, long contactId ){
Predicate p = QProvider.provider.id.eq(providerId).and(QContact.contact.id.eq(contactId));
JPQLQuery query = new JPAQuery(em);
return query.from(QProvider.provider).innerJoin(QProvider.provider.contact).where(p).singleResult(QContact.contact);
}
but I'm getting the following error:
Caused by: java.lang.IllegalArgumentException: Undeclared path 'contact'. Add this path as a source to the query to be able to reference it.
at com.mysema.query.types.ValidatingVisitor.visit(ValidatingVisitor.java:78)
at com.mysema.query.types.ValidatingVisitor.visit(ValidatingVisitor.java:30)
at com.mysema.query.types.PathImpl.accept(PathImpl.java:94)
I'm presuming it has something to do with the fact that my predicate references QContact.contact direction and not part of the QProvider.provider.contact object, but I'm really at a loss as to figure out how this should be done.
Am I even on the right track? I'm not even sure my join is correct either.
This should work
public Contact getContact(long providerId, long contactId) {
QProvider provider = QProvider.provider;
QContact contact = QContact.contact;
return new JPAQuery(em).from(provider)
.innerJoin(provider.contact, contact)
.where(provider.id.eq(providerId), contact.id.eq(contactId))
.singleResult(contact);
}