JpaRepository custom #Query generates unwanted CROSS JOIN - mysql

account entity code:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
#NotFound(action = NotFoundAction.IGNORE)
private User user;
user entity code:
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Account> account = new ArrayList<>();
the repository cusotom method query annotion
#Query(value = "FROM Account a WHERE (:userId IS NULL Or a.user.id = :userId))
It generates this query:
from account account0_ cross join user user1_ where account0_.user_id=user1_.id and (? is null or user1_.id=?)
It worked fine before I updating springboot from 2.1.4.release to 2.6.5
which is expected to be the following:
from account account0_ WHERE (:userId IS NULL Or account0_.user_id = :userId)
Don't know if it's due to the version change or not.
Can someone give some clues?

It's a stange behavior on JPA if you use
#NotFound(action = NotFoundAction.IGNORE)
this above annotion,
remove this will behave normally.

Related

How can i use a mysql query to get an entity and multiple entities that relate to it?

So i have 3 tables: tutorials, topics, and tutorial_topics (maps the many to many relation).
I have this query:
select
t.id,
t.created,
t.created_by,
t.last_modified,
t.last_modified_by,
t.version,
l.name,
l.description,
top
from
tutorials t
inner join
localized_tutorial l
inner join topics as top
where l.name like '%Fish Cooking%' and l.locale='pt';
Now first, it tells me that t.id does not exist (i assume because is a derived table doesnt recognize the t.id from parent query), if i remove t.id verifications, says that topics in the first select is an unknown column, and if i put the join instead of the select (like below) just says i cant return a column with multiple objects. Im trying to send this to a spring boot app that receives this data. Is there any way to fix it?
select
t.id,
t.created,
t.created_by,
t.last_modified,
t.last_modified_by,
t.version,
l.name,
l.description,
(select * from topics topic inner join topics_tutorials ttopic where topic.id = ttopic.topics_id and t.id = ttopic.tutorials_id)
from
tutorials t
inner join
localized_tutorial l
where l.name like '%Java%' and l.locale='pt';
This is my tutorial table:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Table(name = SQL.TUTORIAL_TABLE)
#Entity(name = SQL.TUTORIAL_ENTITY)
#ToString(doNotUseGetters = true, callSuper = true)
#EqualsAndHashCode(doNotUseGetters = true, callSuper = true)
public class TutorialDAO implements Serializable {
#Id
#GeneratedValue(generator = "uuid2", strategy = GenerationType.AUTO)
#Column(columnDefinition = "BINARY(16)", unique = true)
#GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
private UUID id;
#CreatedDate
private LocalDateTime created;
#CreatedBy
private String createdBy;
#LastModifiedDate
private LocalDateTime lastModified;
#LastModifiedBy
private String lastModifiedBy;
#Version
private int version;
private static final long serialVersionUID = -1697137782136090241L;
private boolean exclusive;
#ToString.Exclude
#EqualsAndHashCode.Exclude
#ManyToMany(mappedBy = "tutorials", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
private Set<TopicDAO> topics = new HashSet<>();
#ToString.Exclude
#EqualsAndHashCode.Exclude
#ElementCollection(fetch = FetchType.EAGER)
private Set<SectionDAO> sections = new HashSet<>();
#ToString.Exclude
#EqualsAndHashCode.Exclude
#ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#JoinTable(name = "tutorial_courses", joinColumns = #JoinColumn(name = "tutorial_id"), inverseJoinColumns = #JoinColumn(name = "course_id"))
private Set<CourseDAO> courses = new HashSet<>();
#ToString.Exclude
#EqualsAndHashCode.Exclude
#OneToMany(mappedBy = "tutorial", cascade = { CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST,
CascadeType.REFRESH }, orphanRemoval = true, fetch = FetchType.LAZY)
#MapKey(name = "localizedId.locale")
private Map<String, LocalizableTutorial> localizations = new HashMap<>();
}
And this is my Projection class
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString(doNotUseGetters = true, callSuper = true)
#EqualsAndHashCode(doNotUseGetters = true, callSuper = true)
public class LocalizedTutorialDAO extends AbstractDAO {
private static final long serialVersionUID = -8671921365618993655L;
private String name, description;
#ToString.Exclude
#EqualsAndHashCode.Exclude
private Set<TopicDAO> topics = new HashSet<>();
#ToString.Exclude
#EqualsAndHashCode.Exclude
private Set<SectionDAO> sections = new HashSet<>();
}
The topic entity is just an id and a topic string. i made it an entity in order to avoid duplicates.
My question is: from a tutorialDAO object how can i get a LocalizedTutorialDAO? i already can retrieve name and description from its localizations with the query:
select new com.fullstack.daos.projections.LocalizedTutorialDAO(t.id, t.created, t.createdBy, t.lastModified, t.lastModifiedBy, t.version, (VALUE(l)).name, (VALUE(l)).description) from tutorial t join t.localizations l where (VALUE(l)).name like %:name% and (KEY(l)) = :lang
The thing is when i need topics, it just gives the problems i described. However a spring generated FindById return the whole tutorial with topics, if i try to replicate it tho, nothing happens
From your queries, I think you want to select all columns from table topics but able to do it. Below is the method you can use to fetch them -
select t.id,
t.created,
t.created_by,
t.last_modified,
t.last_modified_by,
t.version,
l.name,
l.description,
top.[1st_col_topics_table],
top.[2nd_col_topics_table],
top.[3rd_col_topics_table],
top.[4th_col_topics_table]
...
from tutorials t
inner join localized_tutorial l on <join condition>
inner join topics_tutorials ttopic on t.id = ttopic.tutorials_id
inner join topics top on top.id = ttopic.topics_id
where l.name like '%Fish Cooking%' and l.locale='pt';
You cannot just write the table alias to fetch all columns. You have to mention all the columns explicitly.

Get children for any level in tree structure but without theirs children using Hibernate?

I have a Folder entity in Hibernate, like so:
#Entity
#Table(name = "folders")
public class Folder {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "folder_id", unique = true, nullable = false)
private int id;
#NonNull
#Column(name = "name", length = 100, unique = true, nullable = false)
private String name;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "parent", orphanRemoval = true)
#Column(name = "sub_folders")
private Set<Folder> childFolders = new HashSet<>();
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JsonIgnore
#JoinColumn(name = "parent", referencedColumnName = "folder_id", nullable = true)
private Folder parent;
public Folder() {
}
}
I'm trying to write a finder method or custom query which will do what I wrote in the subject.
So if I send a request going like folders/{parent_folder_id}, let's say value being 1, I should get objects 4 and 5, but without their children, so not including 6,7,8 and 9.
Ideally, hibernate query would be preferred. If not, any sql language is also fine. I'll try to tumble it up to hibernate.
This is what I got, I still get children...
#Query(value = "Select * from folders f where f.parent = ?1 ", nativeQuery = true)
Set<Folder> getFolders(int folder_id);
I think this should work:
make the default fetchtype lazy:
#OneToMany(fetch = FetchType.EAGER, mappedBy = "parent", orphanRemoval = true)
#Column(name = "sub_folders")
private Set<Folder> childFolders = new HashSet<>();
Use a JOIN FETCH in order to eagerly fetch the relationships you want.
SELECT f FROM folders f JOIN FETCH f.childFolders
You probably can achieve something similar with entity graphs but I'm not sure about their interaction with queries.
I got what I need with following query:
#Query(value = "Select folder_id, name, parent From folders f Where f.parent = ?1", nativeQuery = true)
This will give me just name of the folder and its Id.

Selecting where an entity contains a list which elements have another list

I am writing a JPQL query in spring JPA and i have the following scenario. I have a entity Margin which contains a list of PerPeriodMargin and each element of PerPeriodMargin contains a list of MarginFactor.
code:
#Entity
public class Margin {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "margin", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PerPeriodMargin> perPeriodMargins;
}
#Entity
public class PerPeriodMargin{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
private Margin margin;
#OneToMany(mappedBy = "perPeriodMargin", cascade = CascadeType.ALL, orphanRemoval = true)
private List<MarginFactor> marginFactors;
}
#Entity
public class MarginFactor{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
private Underlying underlying;
#ManyToOne
private PerPeriodMargin perPeriodMargin;
}
I would like to select all Margin where MarginFactor underlying.id is passed as a parameter in a single jpql query?
Any suggestions?
i activated hibernate logging by this line in application.properties "logging.level.org.hibernate.SQL=DEBUG" and i have been confused about the generated SQL queries. its seems there is something wrong about that multiple join. can any one explain this.
select * from margin m inner join per_period_margin ppm on m.id = ppm.margin_id join margin_factor mf on ppm.id = mf.per_period_margin_id where mf.underlying_id = ? and m.id = (select margin_id from trading_account ta where ta.id = ?)
and on for perPeriodMargin
select perperiodm0_.margin_id as margin_i5_12_0_, perperiodm0_.id as id1_12_0_, perperiodm0_.id as id1_12_1_, perperiodm0_.end_time as end_time2_12_1_, perperiodm0_.margin_id as margin_i5_12_1_, perperiodm0_.name as name3_12_1_, perperiodm0_.start_time as start_ti4_12_1_ from per_period_margin perperiodm0_ where perperiodm0_.margin_id=?
until now all is seams good.
finally ther are two other queries that try to get marginFactors.
select marginfact0_.per_period_margin_id as per_peri6_9_0_, marginfact0_.id as id1_9_0_, marginfact0_.id as id1_9_1_, marginfact0_.bid as bid2_9_1_, marginfact0_.notional as notional3_9_1_, marginfact0_.offer as offer4_9_1_, marginfact0_.per_period_margin_id as per_peri6_9_1_, marginfact0_.settlement as settleme5_9_1_, marginfact0_.underlying_id as underlyi7_9_1_, underlying1_.id as id1_24_2_, underlying1_.digits as digits2_24_2_, underlying1_.display as display3_24_2_, underlying1_.enable as enable4_24_2_, underlying1_.enable_buy as enable_b5_24_2_, underlying1_.enable_sell as enable_s6_24_2_, underlying1_.focus_digits as focus_di7_24_2_, underlying1_.focus_position as focus_po8_24_2_, underlying1_.left_currency_id as left_cu11_24_2_, underlying1_.name as name9_24_2_, underlying1_.right_currency_id as right_c12_24_2_, underlying1_.temporary_disable as tempora10_24_2_, currency2_.id as id1_3_3_, currency2_.digits as digits2_3_3_, currency2_.enable_buy as enable_b3_3_3_, currency2_.enable_sell as enable_s4_3_3_, currency2_.name as name5_3_3_, currency2_.symbol as symbol6_3_3_, currency2_.temporary_disable as temporar7_3_3_, currency3_.id as id1_3_4_, currency3_.digits as digits2_3_4_, currency3_.enable_buy as enable_b3_3_4_, currency3_.enable_sell as enable_s4_3_4_, currency3_.name as name5_3_4_, currency3_.symbol as symbol6_3_4_, currency3_.temporary_disable as temporar7_3_4_ from margin_factor marginfact0_ left outer join underlying underlying1_ on marginfact0_.underlying_id=underlying1_.id left outer join currency currency2_ on underlying1_.left_currency_id=currency2_.id left outer join currency currency3_ on underlying1_.right_currency_id=currency3_.id where marginfact0_.per_period_margin_id=?
and as we see here in the last query there are only one where condition on perPeriodmarginId. as I think it must also contain underlying condition, because this query is the responsible of fetching marginFactors where we must extract the data that who have a specific underlyingId.
I'm really serious about this question please can someone explain that or is it a bug in hibernate!
This is the query:
select m
FROM Margin m
JOIN m.perPeriodMargins ppm
JOIN ppm.marginFactors mf
JOIN mf.underlying und
WHERE und.id = :id

Select record where id is one of collection id

In a spring mvc application i have 3 database tables (including one mapping table) and their 2 corresponding java entities.
The entities are:-
public class User {
private Long id;
private String userName;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "user_location",
joinColumns = {#JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "location_id", referencedColumnName = "id")}
)
private Set<Location> location;
}
public class Location {
private Long id;
private String locationCode;
}
Three tables are users, location and user_location.
I want to select a user whose location id is equal to a particular id.
Since user can have multiple locations i am not sure how to write a hibernate query for this. I tried few combinations below but i am either getting a exception,
illegal attempt to dereference collection [{synthetic-alias}{non-qualified-property-ref}] with element property reference [id]
or getting a list of User and Location objects. i just want a list of user objects.
from User where userName = :userName and :locationId in (location.id)
from User user inner join user.location loc where user.userName = :usersName and loc.id = :locationId
Update:
I tried query,
Query query = getCurrentSession().createQuery("from User user inner join user.location loc where user.userName = :usersName and loc.id = :locationId");
Hibernate generates following plain SQL from above query and returns a list of User and Location objects. For example if there is one location for an user that matches above query hibernate returns a list that contains one User and one Location object.
select
user0_.id as id1_18_0_,
location2_.id as id1_5_1_,
user0_.user_name as user_na11_18_0_,
location2_.location_code as location3_5_1_
from
users user0_
inner join
user_location location1_
on user0_.id=location1_.user_id
inner join
location location2_
on location1_.location_id=location2_.id
where
user0_.user_name=?
and location2_.id=?
You could try to use next criteria:
Criteria criteria = getCurrentSession().createCriteria(User.class, "u");
criteria.createAlias("location", "loc");
criteria.add(Restrictions.eq("u.userName", "userName");
criteria.add(Restrictions.eq("loc.id", locationId);
List<User> users = criteria.list();
Or you could try HQL typed query:
TypedQuery<User> query =
getCurrentSession().createQuery("SELECT User.* FROM User u JOIN user_location ul ON u.id = ul.user_id JOIN Location l ON ul.location_id = l.id WHERE u.userName = :userName AND l.id = :locationId", User.class)
.setParameter("userName", "userName")
.setParameter("locationOd", locationId);
List<User> users = query.getResultList();

Data lost when trying to edit record

I'm currently having this issue: I have 3 tables: users, roles and users_roles as in the picture below:
whenever I edit any of the record in users table, the record of that user in the users_roles table will be lost.
For example, I changed the username of the user which holds the userId = 2, then in the users_roles table, the row of userId = 2 will be lost.
Anybody has any ideas of this problem? I'm using Spring with Hibernate
*UPDATE
In my Role.java
#ManyToMany(fetch = FetchType.EAGER, mappedBy = "roles")
private List<User> users;
In my User.java
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "users_roles", joinColumns = #JoinColumn(name = "userId", nullable = false) , inverseJoinColumns = #JoinColumn(name = "roleId", nullable = false) )
private List<Role> roles;
And in my UsersRoles.java
#Id
#Column(name="userId")
private int userId;
#Id
#Column(name="roleId")
private int roleId;
This is the DAO implementation method I used for Edit
#Override
public void edit(User user) {
session.getCurrentSession().saveOrUpdate(user);
}
P/S: this not only happens when I edit with my web-app, but also happens when I edit directly in MySQL environment. I don't know...