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

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.

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.

Query for null and non null at the same time

I've got this entity:
#Getter
#Setter
#NoArgsConstructor
#ToString
#Accessors(chain = true)
#Entity
#Table(name = "offer_categs")
public class OfferCateg {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#Nullable
private String image;
#ManyToOne
#JoinColumn(name = "parent_id")
private OfferCateg parent;
#OneToMany(mappedBy = "parent",
cascade = CascadeType.REMOVE,
fetch = FetchType.LAZY,
orphanRemoval = true)
private Set<OfferCateg> children;
}
I'm trying to just create categories and subcategories. My problem is that when using the below method, parent_id might be null in case of a root category. How can I make the query to allow for that? I can't use IS NULL. Should I make two queries and be careful of the calling parent_id value ?
#Query(value = "SELECT * FROM offer_categs oc WHERE oc.parent_id = ?1 AND oc.name = ?2", nativeQuery = true)
OfferCateg findByNameAndParent(#Nullable Long parentId, String name);
EDIT
I changed the query to this and it seems to be working
#Query(value = "SELECT * FROM offer_categs oc WHERE " +
"(oc.parent_id = ?1 AND oc.name = ?2) OR" +
"(oc.parent_id IS NULL AND ?1 IS NULL AND oc.name = ?2)",
nativeQuery = true)
OfferCateg findByNameAndParent(#Nullable Long parentId, String name);
BUT...isn't this overkill ?! I'm sure it can be done more easily.

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...

Hibernate - Join query

I have two classes:
#Entity
public class Tick {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#ManyToOne(optional = false)
#JoinColumn(name = "elitesystem_id", referencedColumnName = "id")
private EliteSystem eliteSystem;
private Date createDate;
#ManyToOne(optional = true)
#JoinColumn(name = "commander_id", referencedColumnName = "id")
private Commander commander;
private String address;
and
#Entity
public class Note {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#ManyToOne(optional = true)
#JoinColumn(name = "tick_id", referencedColumnName = "id")
private Tick tick;
private String text;
private Date createDate;
I want to select all ticks and get notes if there are any:
Query query = session.createQuery("select t, n from Note n right join n.tick t where t.commander.name = '123'");
List<Object[]> list = query.list();
This returns only Tick objects. What is the correct approach to get the Note information as well in 1 single query?
I could put a reference to a Note into the Tick class, but this doesnt sound right, as there are only a few notes, so the column in the Tick table would mostly be empty.
Create a New class for example:
public class TickNote {
private Tick tick;
private Note note;
public TickNote(Tick tick,Note note){
this.tick=tick;
this.note=note;
Then your query is:
Query query = session.createQuery("select NEW TickNote(t, n) from Note n right join n.tick t where t.commander.name = '123'");
List<TickNote> list = query.list();

Converting SQL query to JPA NamedQuery

I'm trying to implement a Keyword search functionality that returns a List of Keyword entities based on a field text match.
Right now, the query
select * from photo_keywords pk
inner join keywords k on pk.photo_id = k.keyword_id
inner join photos p on pk.keyword_id = p.photo_id
where k.keyword LIKE "%$SOME_SEARCH_VALUE%";
returns all matching photos for a given keyword search. I'd like to have this adapted to a #NamedQuery with the following Entity objects:
#Entity
#Table(name = "keywords")
public class Keyword implements Serializable{
#Id
#Column(name = "keyword_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column
private String keyword;
#ManyToMany(mappedBy = "keywords")
private List<Photo> photos;
//getters and setters
}
and
#Entity
#Table(name = "photos")
public class Photo implements Serializable{
#Id
#Column(name = "photo_id", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "photo_name", nullable = false)
private String photoName;
#Column(name = "photo_path", nullable = false)
private String photoPath;
#Column(name = "upload_date", nullable = false)
private Date uploadDate;
#Column(name = "view_count", nullable = false)
private int viewCount;
#Column(name = "capture_date", nullable = false)
private Date captureDate;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "photo_metadata")
#MapKeyColumn(name = "metadata_name")
#Column(name = "metadata_value")
private Map<String, String> photoMetadata;
#ManyToMany
#JoinTable(name = "photo_keywords",
joinColumns = #JoinColumn(name = "keyword_id"),
inverseJoinColumns = #JoinColumn(name = "photo_id"))
public List<Keyword> keywords;
//getters and setters
}
This creates a join table photo_keywords, rather than a JoinColumn.
What I've tried so far with the Keyword entity:
#NamedQueries({
#NamedQuery(
name = "findKeywordByName",
query = "SELECT keyword from Keyword k WHERE k.keyword = :keyword"
)
})
which is executed via
public Keyword findKeywordByString(String keyword){
Keyword thisKeyword;
Query queryKeywordExistsByName = getEntityManager().createNamedQuery("findKeywordByName");
queryKeywordExistsByName.setParameter("keyword", keyword);
try {
thisKeyword = new Keyword((String) queryKeywordExistsByName.getSingleResult());
} catch (NoResultException e){
thisKeyword = null;
}
return thisKeyword;
}
This returns the Keyword, but with the photos property being null. This is to be expected, since I'm only selecting the keyword property. How can I adapt the SQL query above to a #NamedQuery?