HQL join query: Path expected for join - spring

I'm new to hql, I referred to a site to write hql query in Spring Framework, but it throws "Path expected for join!" exception
My query is
"from GaugeCateSelect cs inner join PreferredUrl purl on cs.survey=purl.survey where purl.uuid=:uuid"
I want to connect both table with "survey".
How can I sort it out?
Update
Two tables, names are GaugeCateSelect and PreferredUrl. The "survey" field is common for both table. uuid is in PreferredUrl. I want to get all data from GaugeCateSelect when I pass the uuid to PreferredUrl table. (In short, Pass uuid to PreferredUrl, then find the survey number from PreferredUrl and check the number with GaugeCateSelect table, if exists get all data)
Update 2
There is no primary/foreign key reference relationship between two tables. but common field survey is there
GaugeCateSelect class
class GaugeCateSelect {
private int id;
private String categoryName;
private int posNeg;
private Survey survey; //survey is in foreign key relationship of survey table
//Annotation, getters and setters were removed for easiness.
}
PreferredUrl class
public class PreferredUrl {
private int preferredUrlId;
private String uuid;
private int enabled;
private Survey survey; //survey is in foreign key relationship of survey table
//Annotation, getters and setters were removed for easiness.
}

Maybe you should check the Hibernate version you use as there is a difference when joining unrelated entities as discussed here: https://www.thoughts-on-java.org/how-to-join-unrelated-entities/

Related

Join 2 tables using Spring JPA

I have two entities(Invoice and InvoiceItems) without adding any relationship.
Invoice
public class Invoice {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long invoiceID;
#Column(name="code")
private String code;
//other columns
}
Invoice Items
public class InvoiceItems {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long invItemID;
#Column(name="invoice_id")
private Integer invoiceId;
//other columns
}
Can I join these entities and get data without adding relationship using JPA?
If it isn't possible how to join 2 entities using JPQL or Native query?
If your data is valid then using native query you can do that
#Query(nativeQuery = true, "select * from Invoice i join InvoiceItems im on i.id = im.invoice_id")
public List<Invoice> findData();
But that is not a good way join without relation using JPA.
Yes, you can join these entities and get data without adding relationship using JPA, but it's a little bit losing the purpose of using JPA.
You need to create a java class first, which will be the returning data object from the DB. After that you can use entityManager's createNamedQuery method to get the result.
createNamedQuery(String sqlString, ResultClass.Class)
sqlString may be something like:
SELECT INV.INVOICE_ID
INV.CODE
INV_ITEMS.INV_ITEM_ID
FROM INVOICE INV
JOIN INVOICE_ITEMS INV_ITEMS
ON INV.INVOICE_ID = INV_ITEMS.INVOICE_ID;
And the corresponding ResultClass:
public class ResultClass {
private Long invoiceID;
private String code;
private Long invItemID;
// other columns
}
Or you can even use RowMapper to map the object all by yourself for more flexibility by using JdbcTemplate with query() method.

Spring Boot JPA Bidirectional Mapping without Foreign Key

Is it possible to Greate DDL using JPA with bidirectional mapping and without foreign key? If can, is it best practice?
#Entity
class Book{
int id;
String title;
#OneToMany(mappedBy="book")
Set<BookDetail> book_detail;
}
#Entity
class BookDetail{
int id;
String name;
String description;
#ManyToOne
Book book;
}
Yes. It is possible using a join table. It will have foreign keys of course.
#Entity
class Book{
#OneToMany
List<BookDetail> bookDetail;
}
#Entity
class BookDetail{
#ManyToOne(fetch = FetchType.LAZY)
Book book;
}
what is #JoinColumn and how it is used in Hibernate
You can't do it without at least one foreign key, since a DB needs to establish some connection between two entities - BookDetail and Book. It is possible to create two tables for these entities without a real foreign key by using plain integer attribute in BookDetail which will be storing a value of Book's id. But don't do that!
With a foreign key your DBMS generates constraints so it's known about the relationship and it prevents some unsafe deletions and insertions, so each BookDetail's row references existing Books one.
Without real foreign key you c accidentally remove a Book and you BookItem's

Spring Data JPA Projection with select distinct

I have a database table which holds Metadata for documents. My task now is to get a list with documenttypes. The documenttypes are not unique in the database table but of course I want them to be in my list. The sql is very simple:
SELECT DISTINCT groupname, group_displayorder
FROM t_doc_metadata
ORDER BY group_displayorder;
I have learned that I can use projections to get a subset of fields from my entity DocMetadata. I solved this as follows. My Entity:
#Entity
#Table(name="T_DOC_METADATA")
#Data
public class DocMetadata {
..............
#Column(nullable=false)
private String displayname;
#Column(nullable=false)
private Integer displayorder;
#Column(nullable=false)
private String groupname;
#Column(name="GROUP_DISPLAYORDER",
nullable=false)
private Integer groupDisplayorder;
#Column(name="METADATA_CHANGED_TS",
nullable=false,
columnDefinition="char")
private String metadataChangedTimestamp;
..........
}
My inteface for projection:
public interface GroupnameAndOrder {
String getGroupname();
Integer getGroupDisplayorder();
void setGroupname(String name);
void setGroupDisplayorder(int order);
}
Now I thought I'd be extraordinary clever by adding these lines to my repository:
#Query("select distinct d.groupname, d.groupDisplayorder from DocMetadata d order by d.groupDisplayorder")
public List<GroupnameAndOrder> findSortedGroupnames();
Sadly, when iterating over the result list and calling getGroupname() the result is null.
So I changed the lines in my repository according to the documentation:
public List<GroupnameAndOrder> findBy();
Now I get the groupnames but of course they are not unique now. So it doesn't solve my problem.
Is there any way to receive a ordered list with unique groupnames?
You are trying to be too clever. Instead just write the proper find method and return the GroupnameAndOrder. Spring Data JPA will then only retrieve what is needed for the projection.
Something like this should do the trick.
List<GroupnameAndOrder> findDistinctByOrderByGroupDisplayorder();

one-way one-to-many throws Hibernate Cannot add or update a child row: a foreign key constraint fails

I have an application that teaches the user how to play various card games. The data model that gets persisted consists of a TrainingSession with a uni-directional one-to-many relationship with the Hands.
[EDIT] To clarify, a Hand has no existence outside the context of a TrainingSession (i.e they are created/destroyed when the TrainingSession is). Following the principals of Data Driven Design, the TrainingSession is treated as an aggregate root and therefore a single spring-data CrudRepository is used (i.e., no repository is created for Hand)
When I try to save a TrainingSession using a CrudRepository, I get: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (blackjack.hand, CONSTRAINT FKrpuxac6b80xc7rc98vt1euc3n FOREIGN KEY (id) REFERENCES training_session (tsid))
My problem is the 'save(trainingSession)' operation via the CrudRepository instance. What I don't understand is why the error message states that FOREIGN KEY (id) REFERENCES training_session (tsid)). That seems to be the cause of the problem but I cant figure out why this is the case or how to fix it. The relationship is uni-directional and nothing in the Hand class refers to the TrainingSession.
The code, minus all the getters and setters, is:
#Entity
public class TrainingSession {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer tsid;
private String strategy;
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="id")
private List<Hand> hands;
private int userId;
protected TrainingSession() {
}
public TrainingSession(int userId, Strategy strategy, List<Hand> hands) {
this.strategy = strategy.getClass().getSimpleName();
this.hands = hands;
this.userId = userId;
}
while Hand is
#Entity // This tells Hibernate to make a table out of this class
public class Hand {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private int p1;
private String p1s;
private int p2;
private String p2s;
private int d1;
private String d1s;
private int trials;
private int score;
public Hand() {
}
You need to save your TrainingSession and Hand objects first before saving the adding the hand objects to TrainingSession.
TrainingSession ts1 = new TrainingSession();
trainingSessionManager.save(ts1);
Hand hand1 = new Hand();
handManager.save(hand1);
Hand hand2 = new Hand();
handManager.save(hand2);
ts1.gethands().add(hand1);
ts1.gethands().add(hand2)
trainingSessionManager.save(ts1);
If you check your database you will find 3 tables TrainingSession, Hand and TrainingSession_Hand, The TrainingSession_Hand table references to both TrainingSession and Hand both. Therefore you need to save TrainingSession and hand before saving the relationship.
Found the problem. I was assuming that when spring-data set up the DB tables, it was able to figure out and set up the uni-directional 1-to-many relationship. Apparently that isn't the case. When I configure the relationship as bi-directional everything seems to work.
To fix things I:
removed from TrainingSession the #joincolumn annotation for hands
in Hands I added a TrainingSession field with a #ManyToOne annotation:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "tsid", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
private TrainingSession tsession;
I also added in the Hand class the getter/setter for tsession
I can now do a save of the entire aggregate construct using only a TrainingSessionRepository.

Sort by joined table's field Spring JPA

I have two entity classes Request,User:
//Ommiting some annotations for brevity
public class User{
private Long id;
private String name;
private Integer age;
}
public class Request{
private Long id;
private String message;
private Date createTime;
#ManyToOne
#JoinColumn(name="user_id")
private User user;
}
I can sort request list by create time :
Sort = new Sort(Direction.ASC,"createTime");
Is there a possible way to sort request list by User's name? Like:
Sort = new Sort(Direction.ASC,"User.name");
Yes. new Sort(Direction.ASC,"user.name"); should work just fine.
Spring Data JPA will left outer join the User to the Request and order by the joined column's name resulting in SQL like this:
select
id, message, createTime
from
Request r
left outer join User u on u.id = r.user_id
order by
u.name asc
This works great on one-to-one and, like you have here, many-to-one relationships but because a left outer join is employed to implement the order by clause, if the joined entity represents a many relationship (like in a one-to-many), the Sort may result in SQL in which duplicate records are returned. This is the case because the Sort parameter will always result in a new left outer join even if the entity being joined is already joined in the query!
Edit Incidentally, there is an open ticket concerning this issue: https://jira.spring.io/browse/DATAJPA-776

Resources