Need solution for following scenario in Hibernate many to many mapping - spring

Consider the tables where posts and tags exhibit a many-to-many relationship between each other.
The many-to-many relationship is implemented using a third table called post_tags which contains the details of posts and their associated tags.
Post Model
#Entity
#Table(name = "posts")
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Size(max = 100)
#Column(unique = true)
private String title;
#NotNull
#Size(max = 250)
private String description;
#NotNull
#Lob
private String content;
#NotNull
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "posted_at")
private Date postedAt = new Date();
#NotNull
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "last_updated_at")
private Date lastUpdatedAt = new Date();
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "post_tags",
joinColumns = { #JoinColumn(name = "post_id") },
inverseJoinColumns = { #JoinColumn(name = "tag_id") })
private Set<Tag> tags = new HashSet<>();
public Post() {
}
public Post(String title, String description, String content) {
this.title = title;
this.description = description;
this.content = content;
}
// Getters and Setters (Omitted for brevity)
}
TAG Model
#Entity
#Table(name = "tags")
public class Tag {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Size(max = 100)
#NaturalId
private String name;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
},
mappedBy = "tags")
private Set<Post> posts = new HashSet<>();
public Tag() {
}
public Tag(String name) {
this.name = name;
}
// Getters and Setters (Omitted for brevity)
}
Problem is
I tried to use an existing tags. and insert happened only on posts & posts_tags table.
Initially i'm Get tag(s) with tagName(s). Once you have the Tag object, you can set it in the Post object and save it.
Like this
Post post = new Post("Hibernate Many to Many Example with Spring Boot",
"Learn how to map a many to many relationship using hibernate",
"Entire Post content with Sample code");
// Create two tags
Tag tag1 = tagService.getTag("Spring Boot");
// Add tag references in the post
post.getTags().add(tag1);
postRepository.save(post);
If I do like that, entry is not available in post_tags table.
Tag Repository and Tag Service:
#Repository
public interface TagRepository extends JpaRepository<Tag, Long> {
#Query("select p from Tag p where p.name = :name")
Tag findByName(#Param("name") String name);
}
#Override
public Tag findByName(String name) {
return repository.findByName(name);
}

Related

how to add object with fk to table in jparepository

i had scheme of user parking and detail parking.
user can park many times (one to many)
im trying to add detail parking object to my db, but i dont have idea how to add the fk from the user in the row of the table, its gave me null there.
(ignore from the logic of the model, i just want to understood the logic how can i the object with fk of ther entity)
this is my code:
#PostMapping("/parking")
public String saveCarParking(#ModelAttribute("user") parkingUsers parkingUsers) {
// parkingUsers[id, firstName, lastName, license]
parkingUsers p = new parkingUsers("jhon", "nash", "248651355");
parkingUsersService.saveParkingUser(p);
// parkingDetails[id, entryDate, entryTime, exitDate, exitTime, user_id(FK)]
parkingDetails d = new parkingDetails(LocalDate.now(), null, LocalDate.now(), null);
parkingDetailsService.saveParkingUser(d);
//how i connect parkingDetails object with fk of parkingUsers?
//it adding now row of parkingDetails but without the fk of user
return "redirect:/parkingList";
}
parking user entity:
#Entity
#Table(name ="users")
public class parkingUsers {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "license")
private String license;
#OneToMany(mappedBy = "parkingUsers", cascade = CascadeType.ALL, orphanRemoval = true)
private List<parkingDetails> parkingDetails = new ArrayList<parkingDetails>();
public parkingUsers() {
}
public parkingUsers(String firstName, String lastName, String license) {
this.firstName = firstName;
this.lastName = lastName;
this.license = license;
}
//setter gettrs and tostring...
entity class of details parking
#Entity
#Table(name ="details")
public class parkingDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "entry_date")
private LocalDate entryDate;
#Column(name = "entry_time")
private LocalDateTime entryTime;
#Column(name = "exit_date")
private LocalDate exitDate;
#Column(name = "exit_time")
private LocalDateTime exitTime;
#ManyToOne
#JoinColumn(name="user_id")
private parkingUsers parkingUsers;
public parkingDetails() {}
public parkingDetails(LocalDate entryDate, LocalDateTime entryTime, LocalDate exitDate, LocalDateTime exitTime) {
this.entryDate = entryDate;
this.entryTime = entryTime;
this.exitDate = exitDate;
this.exitTime = exitTime;
}
//test
// public parkingDetails(LocalDate entryDate, LocalDateTime entryTime, LocalDate exitDate, LocalDateTime exitTime, int user_id ) {
// this.entryDate = entryDate;
// this.entryTime = entryTime;
// this.exitDate = exitDate;
// this.exitTime = exitTime;
// this.parkingUsers.setId(user_id);
// }
//setter gettrs and tostring...
In the ParkingDetails entity, you can have a setter for "parkingUsers" variable to set user object.
In your REST api's saveCarParking() method, before calling "parkingDetailsService.saveParkingUser(d);" you can pass the user object to ParkingDetails using setter created in ParkingDetails.
This should work. No need to explicitly extract the user_id from user's object to pass into ParkingDetails.
Adding one more parameter of type ‘parkingUsers‘ in the constructor of ‘ ParkingDetails’ to initialize user in parking class will also work.
(Apart, it is a good practice to start the class name with a capital letter e.g. instead of having class name as parkingDetails, it should be ParkingDetails.)

How can I retrieve all the children of a record in this Hibernate #ManyToOne relation?

I am working on a Spring Boot project using Spring Data JPA and Hibernate mapping. I have the following doubt about how can I implement the following query.
I have an User entity class like this:
#Entity
#Table(name = "portal_user")
#Getter
#Setter
public class User implements Serializable {
private static final long serialVersionUID = 5062673109048808267L;
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name = "first_name")
#NotNull(message = "{NotNull.User.firstName.Validation}")
private String firstName;
#Column(name = "middle_name")
private String middleName;
#Column(name = "surname")
#NotNull(message = "{NotNull.User.surname.Validation}")
private String surname;
#Column(name = "sex")
#NotNull(message = "{NotNull.User.sex.Validation}")
private char sex;
#Column(name = "birthdate")
#NotNull(message = "{NotNull.User.birthdate.Validation}")
private Date birthdate;
#Column(name = "tax_code")
#NotNull(message = "{NotNull.User.taxCode.Validation}")
private String taxCode;
#Column(name = "e_mail")
#NotNull(message = "{NotNull.User.email.Validation}")
private String email;
#Column(name = "pswd")
#NotNull(message = "{NotNull.User.pswd.Validation}")
private String pswd;
#Column(name = "contact_number")
#NotNull(message = "{NotNull.User.contactNumber.Validation}")
private String contactNumber;
#Temporal(TemporalType.DATE)
#Column(name = "created_at")
private Date createdAt;
#Column(name = "is_active")
private boolean is_active;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval = true)
#JsonManagedReference
private Set<Address> addressesList = new HashSet<>();
#ManyToMany(cascade = { CascadeType.MERGE })
#JoinTable(
name = "portal_user_user_type",
joinColumns = { #JoinColumn(name = "portal_user_id_fk") },
inverseJoinColumns = { #JoinColumn(name = "user_type_id_fk") }
)
Set<UserType> userTypes;
#ManyToOne(fetch = FetchType.EAGER)
#JsonProperty("subagent")
private User parent;
public User() {
super();
}
public User(String firstName, String middleName, String surname, char sex, Date birthdate, String taxCode,
String email, String pswd, String contactNumber, Date createdAt, boolean is_active) {
super();
this.firstName = firstName;
this.middleName = middleName;
this.surname = surname;
this.sex = sex;
this.birthdate = birthdate;
this.taxCode = taxCode;
this.email = email;
this.pswd = pswd;
this.contactNumber = contactNumber;
this.createdAt = createdAt;
this.is_active = is_active;
}
}
The instances of this class represents users of my system. An user can have a single specific parent (the concept is similar to that of a referral: an user can bring another user in the system). This is handled by this ManyToOne recursive relationship:
#ManyToOne(fetch = FetchType.EAGER)
#JsonProperty("subagent")
private User parent;
Basically an user contains is parent (who bring him\her into the platform). It works fine. So retrieving an user I can easily retrieve the information of who is its parent (it is contained into the retrieved User object).
Now I need to implement the inverse behavior: I have to define a "query" that starting from a parent retrieve all its children.
The previous User entity class maps the following DB table:
The highlighter parent_id contains the FK that define this recursive relationship. So it contains the PK of another user that is the parent.
I have this UserRepository repository interface (it extents the JpaRepository interface)
public interface UsersRepository extends JpaRepository<User, Integer> {
User findByemail(String email);
List<User> findByUserTypes_TypeName(String typeName);
}
As you can see I am using a "query by method" style. Is it possiblem implement a behavior like this using "query by method" style? (in case also JPQL could be fine)
You can do this
List<User> findByParent_Id(Integer id);
Or you can do this
#Query("SELECT u FROM User u WHERE u.id = ?1")
List<User> getReferredUsers(Integer id);
The relationship between the user and the parent is unidirectional in the given code. By making it bidirectional, it is easy to query the data in either ways.
Refer to below code to make it bidirectional. Also ensure the relevant FetchType to avoid the performance risk. Here FetchType.LAZY is used for one to many association so it queries the data using the proxy reference when needed.
#ManyToOne(fetch = FetchType.EAGER)
#JsonProperty("subagent")
#JsonBackReference
private User parent;
#JsonManagedReference
#OneToMany(fetch = FetchType.LAZY, mappedBy = "parent")
private Set<User> userSet = new HashSet<>();
Child entities are fetched only when parent.getUserSet is used because of the FetchType.Lazy
public Set<User> getUsers(int id) {
User parent = userRepository.getById(id);
return parent.getUserSet();
}

Can I get the list of objects in relation #OneToMany?

I've got 2 classes: Device and Category. 1 Device can have 1 assigned category, but 1 category can have assigned many different devices.
#Entity
#Data
#Table(name = "devices")
public class Device implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
#Column(name="amount_of_items")
private Integer amountOfItems;
private BigDecimal price;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "category_id")
private Category category;
public Device(String name, String description, Integer amountOfItems, BigDecimal price, Category category){
this.name = name;
this.description = description;
this.amountOfItems = amountOfItems;
this.price = price;
this.category = category;
}
public Device() {}
}
#Entity
#Data
#Table(name = "categories")
public class Category implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
#OneToMany(mappedBy = "category", fetch = FetchType.EAGER)
private List<Device> devices = new ArrayList<>();
public Category(String name, String description){
this.name = name;
this.description = description;
}
public Category() { }
}
Can I get the actual list of devices for one Category? The below code returns me a null list of devices:
Category category = new Category("Urzadzenia AGD", "tylko dla klientow premium");
categoryRepository.save(category);
Device device = new Device("pralka", "samoobslugowa", 50, new BigDecimal("220"),
category);
deviceRepository.save(device);
System.out.println(category.getDevies()) ---> returns NULL
Can I do it by calling a getter like above?
save method already return value after save in Database you can use this
Category category = new Category("Urzadzenia AGD", "tylko dla klientow premium");
category= categoryRepository.save(category);
Device device = new Device("pralka", "samoobslugowa", 50, new BigDecimal("220"),
category);
deviceRepository.save(device);
System.out.println(category.getDevies())
and you must be make setter and getter method in your class
after this you have problem stackover flow exciption becouse the all device called category and categore call Devices
you can used #JsonIgnore annotation
Like this :
#OneToMany(mappedBy = "category", fetch = FetchType.EAGER)
#JsonIgnore
private List<Device> devices = new ArrayList<>();

jpa - list OneToMany not saved

I have the below room entity which has many disponibilities, when i add a room with a list of disponibilities, the room is saved but the list is not. what am i missing in the relationship ?
#Entity
public class RoomEntity {
#Id
private String classRoomId;
private String label;
#OneToMany(mappedBy = "room", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<DisponibilityEntity> disponibilities;
public void addDisponibilities(List<DisponibilityEntity> disponibilityEntities) {
if (CollectionUtils.isEmpty(disponibilities)) {
disponibilities = new ArrayList<>();
}
disponibilities.addAll(disponibilityEntities);
disponibilityEntities.forEach(item -> item.setRoom(this));
}
}
#Entity
public class DisponibilityEntity {
#Id
private String disponibilityId;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "fk_room")
private RoomEntity room;
}
roomEntity.addDisponibilities(classRoomEntity.getDisponibilities());
roomRepository.save(roomEntity);

Spring Data JPA: How to fetch all entities of a specific type along with each entity's associated entities?

I have a Post entity
#Entity
public class Post {
#Id
private UUID id;
#NotNull
private String title;
#NotNull
private String content;
#NotNull
private String identifier;
#NotNull
private String category;
#NotNull
#Column(name = "created_at")
private Date createdAt;
#NotNull
#Column(name = "updated_at")
private Date updatedAt;
public Post (){
}
public Post (String title, String content, String category){
this.title = title;
this.content = content;
this.category = category;
}
// rest of the getters and setters
}
And this is my Comment entity:
#Entity
public class Comment {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private UUID id;
#NotNull
private String name;
#NotNull
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer identifier;
#NotNull
private String email;
#NotNull
private String content;
#NotNull
#ManyToOne
#JoinColumn(name = "post_id")
private Post postId;
#NotNull
#Column(name = "created_at")
private Date createdAt;
public Comment() {
}
public Comment(String name, String email, String content){
this.name = name;
this.email = email;
this.content = content;
}
}
And this is my post controller:
#RestController
#RequestMapping("/posts")
public class PostController {
private String getIdentifier(String str){
return String.join("-", str.split(" "));
}
#Autowired
private PostService postService;
#RequestMapping(value = "", method = {GET, HEAD})
public List<Post> getAllPosts(){
return postService.getAllPosts();
}
#RequestMapping(value = "", method = {POST, OPTIONS})
public Post addNewPost(#RequestBody Post post){
post.setId(UUID.randomUUID());
post.setIdentifier(this.getIdentifier(post.getTitle()));
post.setCreatedAt(new Date());
post.setUpdatedAt(new Date());
return postService.savePost(post);
}
#RequestMapping(value = "/{id}", method = {GET, HEAD})
public Post getOnePost(#PathVariable UUID id){
return postService.getOne(id);
}
#RequestMapping(value = "/{id}", method = DELETE)
public void deleteOnePost(#PathVariable UUID id){
postService.deleteOnePost(id);
}
}
My question is how do I fetch all the comments for each individual post, whenever I fetch all the posts?
Sorry, I come from a NoSQL background, so this is a bit daunting at first.
What you need to do is to create a bidirectional #OneToMany association from the Post to Comments:
add a field in Post class
#OneToMany(
mappedBy = "postId",
cascade = CascadeType.ALL
)
private List<Comments> comments = new ArrayList<>();
From now on, when you get Post from the database, Comments will be fetched at the same time.

Resources