Hibernate #ManyToMany association table with extra field that can be queried differently - java

I was trying to do #ManyToMany association and it worked when I tried to do relations like
User can have multiple group and one group can multiple user.. it worked ,and hibernate created custom table based on it automatically and it did its worked. later I had to add more columns to the association table so I followed a article and set the things up as per that, which worked pretty good.
I am using SpringBoot and is using SpringDataJPA
Here is my implementation :
#Entity
#Table(name = "USERS")
public class User {
#Id
#GeneratedValue
private long id;
private String username;
private String password;
private String email;
#OneToMany(mappedBy = "user")
private Set<UserGroup> userGroups = new HashSet<UserGroup>();
}
#Entity
#Table(name = "GROUPS")
public class Group {
#Id
#GeneratedValue
private long id;
private String name;
   #OneToMany(mappedBy = "group")
private Set<UserGroup> userGroups = new HashSet<UserGroup>();
}
#Entity
#Table(name = "USERS_GROUPS")
public class UserGroup {
#Id
#GeneratedValue
private long id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "USER_ID")
private User user;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "GROUP_ID")
private Group group;
// additional fields
private boolean activated;
private Date registeredDate;
}
User user = new User("tommy", "ymmot", "tommy#gmail.com");
Group group = new Group("Coders");
User persistedUser = userRepository.save(user);
Group persistedGroup = groupRepositry.save(group);
UserGroup userGroup = new UserGroup();
userGroup.setGroup(persistedGroup);
userGroup.setUser(persistedUser);
userGroup.setActivated(true);
userGroup.setRegisteredDate(new Date());
userGroupRepository.save(userGroup);
Now how to write a SpringData equavalent methd name for getting user's
group where the user is active ? i.e I make user active = false when
some one deletes users from a group instead of deleting the entry from
the user_group assossiation table.
Can we do it on the userRepository?

I think that you would like to have repository similar to this one:
public interface UserGroupRepository extends JpaRepository<UserGroup, Long> {
List<UserGroup> findAllByUserAndActivatedIsTrue(User user);
}
This method will give you List of all groups that this user is assigned to and is active.
If you would like to parameterize also activated field, you should instead use
List<UserGroup> findAllByUserAndActivated(User user, boolean activated);
I hope that this helps you. Good luck.
And btw, I recommend reading this:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods
Helps a lot

Related

Hibernate remove just the relation, not the entity on the relation

I have a one to many relation on post class, and on the relation table I have one to one relation with user. Everything works find, but i want to be able to remove the relation, keeping the user entity, is that possible?
At this moment with the annotation orphanRemoval = true when I remove from post Detail list an element, this its removed from post_details table but the user is removed too.
#Entity
#Table(name = "ta_post")
public class Post{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private Date fcDate;
#OneToMany(mappedBy="post", cascade=CascadeType.ALL, orphanRemoval = true)
private List<PostDetails>;
}
#Entity
#Table(name = "ta_user")
public class User{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int mail;
}
#Entity
#Table(name = "ta_post_details")
public class PostDetails{
private int id;
#ManyToOne
#JoinColumn(name="post_id")
private Post post;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="user_id")
private User user;
private String postComments;
}
You must remove the CascadeType.ALL from the PostDetails. If you want to be able to change the User through the PostDetails, you can set the CascadeType to PERSIST or MERGE. If you want to create a PostDetail along with an User, you need to include the CascadeType CREATE.
I'd guess you are creating the user somewhere else and you just associate one with a Post, so removing the CascadeType.ALL should be enough to not delete your User from the database.

Spring oneToOne relationship not setting FK when saving

this is my code:
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER, optional = false)
private Employee employee;
public User() {
}
//SETTERS AND GETER
}
#Entity
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private User user;
public Employee() {
}
//SETTERS AND GETER
}
On my service I'm trying somthing like this:
User user = new User();
user.setUsername("my_username");
user.setPassword("12345");
Employee employee = new Employee();
employee.setName("My Name");
employee.setBirth(LocalDate.now());
user.setEmployee(employee);
userService.save(user);
There Are no errors nor any problem on the application, but looking on my database, the user_id column is empty, what is for me to do to have user_id set automatically with User id? Thanks in advance!
As it is stated in the hibernate documentation:
Whenever a bidirectional association is formed, the application developer must make sure both sides are in-sync at all times.
You use bidirectional #OneToOne so, you should synchronize both side of the association:
User user = new User();
user.setUsername("my_username");
user.setPassword("12345");
Employee employee = new Employee();
employee.setName("My Name");
employee.setBirth(LocalDate.now());
// make both side of bidirectional #OneToOne in-sync
user.setEmployee(employee);
employee.setUser(user);
userService.save(user);

Why spring data jpa-java.lang.IllegalStateException: Multiple representations of the same entity in OneToMany relationship?

I am working a very small application which contains 3 entity classes.
1.Category.
2.Products.
3.User
Relationships:-
a. OneToMany between User and Products.
b. OneToMany and ManyToOne between category and products i.e. a category can have multiple products and multiple products can belong to same category.
Entity Classes are shown below:-
User Entity:-
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String username;
private String lastname;
private String email;
private String password;
#OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE},
fetch = FetchType.EAGER)
private Set<Products> products;
//getter and setter
}
Products Entity:-
#Entity
public class Products {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String productname;
private String cost;
#ManyToOne(cascade = {CascadeType.MERGE,CascadeType.PERSIST},
fetch = FetchType.LAZY)
private Category category;
//getter and setters
}
Category Entity:-
#Entity
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
#OneToMany(mappedBy = "category",
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
fetch = FetchType.EAGER)
private List<Products> products;
//getter and setters
}
Method to merge User with products in data base:-
#GetMapping("/cart")
public String Cart(Model model){
model.addAttribute("cart",productsSet);
System.out.println(productsSet);//At this stage in console I am able to see products added in set
User user = userRepository.findById(1);//hard coded for now.
user.setProducts(productsSet);
userService.saveUserProducts(user);//saveUserProducts() method in shown below.
productsSet.clear();
return "mycart";
}
saveUserProducts() :-
#Override
#Transactional
public void saveUserProducts(User user) {
entityManager.merge(user);
}
But when I am running the program I see the following exception in console:-
java.lang.IllegalStateException: Multiple representations of the same entity [com.demo.shopping.com.Entity.Products#2] are being merged. Detached: [Products{id=2, productname='p2', cost='200'}]; Detached: [Products{id=2, productname='p2', cost='200'}]
I found an article on stack-overflow but it was not fit in my situation.(java.lang.IllegalStateException: Multiple representations of the same entity with #ManyToMany 3 entities).Except this I don't get any relevant thing.
Please help me to let me know how to deal with this situation. Hope someone will help.
Thanks in advance.
Remove CascadeType.MERGE user class because in my program I am not adding new products also except this I am creating relation between existing user and products.

Hibernate: ManyToMany unidirectional only returning one record

I have this problem and I'd appreciate any help...
I have a User entity. A user has a Role (a many to one relationship). And a role has a list of permissions (the many to many relationshp I'm having trouble with).
In my code, I am selecting a single user out of the database, by username.
When I retrieve the user, the user's role is there inside of it. But inside the role, there should be a list of permissions. There should be 4 permissions, but every time, I am only getting one element back in the set.
I have queried the database correctly, and there are indeed 4 permissions attached to that role, so the problem isnt there.
UserEntity:
#Table(name = "users")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotEmpty
private String username;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "role_id")
private RoleEntity role;
}
RoleEntity:
#Table(name = "roles")
public class RoleEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotEmpty
private String name;
private String description;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "roles_permissions", joinColumns = #JoinColumn(name = "permission_id"),
inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<PermissionEntity> permissions = new HashSet<>();
}
And here is my repository. I'm calling this method from a service, and am just getting back what I described above.
#Repository
public interface UserRepository extends CrudRepository<UserEntity, String> {
Optional<UserEntity> findByUsername(String username);
}
In my database, I have a table called 'roles_permissions' with the fields role_id and permission_id. There are four records in here. role_id is '1' for all of them, and its linked to permissions 1,2,3,4.
In the users table, there is a field for role_id. And the user that I'm selecting has this field populated with 1.
Okay, nevermind. I've been working on this for a while, and I figured it out as soon as I posted this!
In the #JoinTable annotation, I had "role_id" and "permission_id" the wrong way around!

Hibernate not setting foreign key

I'm trying to learn Hibernate with this simple example but I'm having so trouble with the foreign key which remains "null" in the database.
#Entity
#Table(name = "tb1")
public class Track {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id_tb1", unique= true)
private int id_tb1;
#Column(name = "title")
private String title;
#ManyToOne
#JoinColumn(name="id_tb2")
private tb2 cd;
And this is the second class
#Entity
#Table(name = "tb2")
public class CD {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id_tb2", unique = true)
private int id_tb2;
#Column(name="title")
private String title;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL,mappedBy = "cd")
private List<tb1> tracks = new ArrayList<tb1>();
I save like this:
SessionFactory factory = new Configuration().configure("/resources/hibernate.cfg.xml").buildSessionFactory();
Session session1 = factory.openSession();
session1.beginTransaction();
session1.save(tb2);
session1.getTransaction().commit();
but when Isavethe id_tb2 (in the table tb1) is not set and it remains null. What I'm missing?
The problem you have to set the relation on both sides for a bidirectional relationship.
So you have to set your relationship forCD and your Track object and persist/merge them afterwards.
Without seeing to much of your code you have to do something like.
cd.getTracks().add(track);
track.setCD(cd);
session1.save(track);
session1.save(cd);
See another question for more details.
I think your type of the table2
private tb2 cd;
should be changed as
private CD cd;

Categories