Hibernate One to Many relation- Child table not updating - java

Here I am adding a list of new orders to a specific user. While adding orders to a specific user, it returns success status, but still my database is empty. Why the data is not getting added...?
Users
#Entity
Class Users{
#Id
private int id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
private List<Orders> orders;
}
Orders
#Entity
Class Orders{
#Id
private int id;
private Date orderDate;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private User user;
}
Repository
public ResponseEntity<String> addOrder(int userId,List<Orders> orders) throws UserNotFound{
User user =userRepository.findById(userId).orElse(null);
if(user==null) throw new UserNotFound("User Not Found");
for(Orders o:orders){
o.setOrderDate(new Date("....."));
}
user.getOrders().addAll(orders);
userRepository.save(user);
}

Try to add cascade persist to your relation specification:
#Entity
Class Users{
#Id
private int id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade={CascadeType.PERSIST})
private List<Orders> orders;
}

Related

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);

Selecting specific columns in many to many relationship with JPA

I have to Models for two Entities in Database. Model User and Model Role. These two entities have a many-to-many relationship. In the model files I have class User.java:
#Entity
#Table(name = "User")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String username;
private String password;
private String name;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(
name = "role_user",
joinColumns = {#JoinColumn(name = "user_id")},
inverseJoinColumns = {#JoinColumn(name = "role_id")})
private Set<Role> roles;
//getters and setters
}
and I have class Role.java:
#Entity
#Table(name = "Role")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
private int id;
private String role;
#ManyToMany(mappedBy = "roles", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<User> users;
//getters and setters
}
I also have the repositories/DAO for them:
public interface UserRepository extends JpaRepository<User, Integer>{}
and
public interface RoleRepository extends JpaRepository<Role, Integer> {}
Now when I fetch all results from class Role it returns all the columns from Entity User, how can I specify that I want only the column name from User Entity? Should I build another model only with the columns I need or there is any simple way to select specific columns?

How to fix ' org.hibernate.TransientPropertyValueException'?

I'm setting up client side shopping cart in my web application. All was ok before adding Shopping Cart class and his service. Now when I try to start the Spring application this error is shown:
Caused by: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.myBookstoreProject.domain.security.UserRole.role -> com.myBookstoreProject.domain.security.Role
I searched for a solution but what I found is a problem with entities of my application. A solution was to add (cascade=CascadeType.ALL) to the entities that are causing the error. But my classes already have it and all was good before Shopping Cart class.
User class:
#Entity
public class User implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", nullable = false, updatable = false)
private Long id;
private String username;
private String password;
private String firstName;
private String lastName;
#Column(name = "email", nullable = false, updatable = false)
private String email;
private String phone;
private boolean enabled = true;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JsonIgnore
private Set<UserRole> userRoles = new HashSet<>();
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private List<UserShipping> userShippingList;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private List<UserPayment> userPaymentList;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "user")
private ShoppingCart shoppingCart;
// getters and setters..
}
Role
#Entity
public class Role {
#Id
private int roleId;
private String name;
#OneToMany(mappedBy = "role", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<UserRole> userRoles = new HashSet<UserRole>();
// getters and setters..
}
UserRole class:
#Entity
#Table(name = "user_role")
public class UserRole {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long userRoleId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "role_id")
private Role role;
// getters and setters..
}
Shopping Cart:
#Entity
public class ShoppingCart {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private BigDecimal GrandTotal;
#OneToMany(mappedBy="shoppingCart", cascade=CascadeType.ALL, fetch=FetchType.LAZY)
#JsonIgnore
private List<CartItem> cartItemList;
#OneToOne(cascade=CascadeType.ALL)
private User user;
// getters and setters...
}
Shopping Cart Service implementation:
#Service
public class ShoppingCartServiceImpl implements ShoppingCartService {
#Autowired
private CartItemService cartItemService;
#Autowired
private ShoppingCartRepository shoppingCartRepository;
#Override
public ShoppingCart updateShoppingCart(ShoppingCart shoppingCart) {
BigDecimal cartTotal = new BigDecimal(0);
List<CartItem> cartItemList = cartItemService.findByShoppingCart(shoppingCart);
for (CartItem cartItem : cartItemList) {
if (cartItem.getBook().getInStockNumber() > 0) {
cartItemService.updateCartItem(cartItem);
cartTotal = cartTotal.add(cartItem.getSubtotal());
}
}
shoppingCart.setGrandTotal(cartTotal);
shoppingCartRepository.save(shoppingCart);
return shoppingCart;
}
}
User Service implementation:
In this class method I added "#Transactional" and 5 lines of Shopping Cart and then the error
#Override
#Transactional
public User createUser(User user, Set<UserRole> userRoles) throws Exception {
User localUser = userRepository.findByUsername(user.getUsername());
if (localUser != null) {
LOG.info("user {} already exists. Nothing will be done.", user.getUsername());
} else {
for (UserRole ur : userRoles) {
roleRepository.save(ur.getRole());
}
user.getUserRoles().addAll(userRoles);
ShoppingCart shoppingCart = new ShoppingCart(); // 1
shoppingCart.setUser(user); // 2
user.setShoppingCart(shoppingCart); // 3
user.setUserShippingList(new ArrayList<UserShipping>()); //4
user.setUserPaymentList(new ArrayList<UserPayment>()); // 5
localUser = userRepository.save(user);
}
return localUser;
}
This error terminates Spring application and only creates tables in MySql without adding rows.
Edit 1:
The problem occurs when I try to add a new user to my application. This is my boot main class:
#SpringBootApplication
public class BookstoreProjectApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(BookstoreProjectApplication.class, args);
}
#Autowired
private UserService userService;
#Override
public void run(String... args) throws Exception {
User user1 = new User();
user1.setFirstName("New");
user1.setLastName("User");
user1.setUsername("j");
user1.setPassword(SecurityUtility.passwordEncoder().encode("p"));
user1.setEmail("newUser#gmail.com");
Set<UserRole> userRoles = new HashSet<>();
Role role1 = new Role();
role1.setRoleId(1);
role1.setName("ROLE_USER");
userRoles.add(new UserRole(user1, role1));
userService.createUser(user1, userRoles);
}
}
If I comment method body(run), server runs very well until a new user should be created, then the error appears.
You are persisting the roles from your userRole and then assigning them to the user, but you don't assign the persisted entities to the roles after saving them, thus the roles in userRole are not the same as the persisted ones anymore and also do not have the generated id. When you save an Entity and then add it or a parent to another Entity as a value and not have full cascading, you are adding a different Object. This means, use the return Object from save and reassign it to the object you saved and then it should be fine, or use cascading everywhere and only save 1 object.
TLDR; userRoles' role is not the same as the Role Entities in your Database.
EDIT 1:
Change Set<UserRole> userRoles to List<UserRole> userRoles (otherwise you have to convert it like 100 times since you cannot replace the value of a Set while traversing it afaik) and then replace
for (UserRole ur : userRoles) {
roleRepository.save(ur.getRole());
}
with
for (int i = 0; i < userRoles.size(); i++) {
userRoles.get(i).setRole(roleRepository.save(userRoles.get(i).getRole())
}

Why OneToOne relation doesn't works as expected?

I have 2 entitie. User:
#Table(name = "USERS")
#Entity
public class User {
#Column(name = "user_id")
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private String email;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user")
private Authentication authentication;
}
and Authentication:
#Table(name = "AUTHENTICATIONS")
#Entity
public class Authentication {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "login_id")
private String loginId;//openId login
#JsonIgnore
private String password;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private User user;
}
And I have service which provides registration for new users:
#Override
#Transactional
public User createUser(UserRegistrationForm form) {
Authentication authentication = new Authentication();
authentication.setPassword(form.getPassword());
User user = new User();
user.setAuthentication(authentication);
user.setEmail(form.getEmail());
user.setName(form.getLogin());
authentication.setUser(user);
return userRepository.save(user);
}
And my problem is method userRepository.save() returns infinitely nested objects:
{"id":1,"name":"myName","email":"myemail#gmail.com","authentication":{"id":1,"loginId":null,"user":{"id":1,"name":"myName","email":"myemail#gmail.com","authentication":{"id":1,"loginId":null,"user":{"id":1,"name":"myName","email":"myemail#gmail.com","authentication":{"id":1,"loginId":null,"user":{"id":1,"name":"myName","email":"myemail#gmail.com","authentication":{"id":1,"loginId":null,"user":{"id":1,"name":"myName","email":"myemail#gmail.com","authentication":{"id":1,"loginId":null,"user":{"id":1,"name":"
What am I doing wrong? Help to understand how it should work.
it's json which returns nested objects ... not your repository !
you are using jackson ?
add #JsonManagedReference on one side and #JsonBackReference on the other
Your problem is:
user.setAuthentication(authentication);
...
authentication.setUser(user);
You have a nested reference between user and authentication

Hibernate create alias on many to many list

I have four class; UserGroup, UserAccount, Role, UserGroupRoleRelation and my db is IBM DB2
#Entity
#Table(name = "USER_GROUP")
public class UserGroup implements Serializable {
#Id
#Column(name = "USER_GROUP_ID")
#GeneratedValue
private Long id;
......
..
#OneToMany(mappedBy = "userGroup", cascade = CascadeType.ALL, orphanRemoval = true)
private List<UserGroupRoleRelation> userAccountsRole = new ArrayList<UserGroupRoleRelation>();
}
#Entity
#Table(name = "ROLE")
public class Role implements Serializable {
#Id
#Column(name = "ROLE_ID")
#GeneratedValue
private Long id;
......
#OneToMany(mappedBy = "role")
private List<UserGroupRoleRelation> userAccountInGroup = new ArrayList<UserGroupRoleRelation>();
}
#Entity
#Table(name = "USER_GROUP_ROLE_LINE", uniqueConstraints = #UniqueConstraint(columnNames = { "ROLE_ID", "USER_GROUP_ID" }))
public class UserGroupRoleRelation {
#Id
#GeneratedValue
#Column(name = "RELATION_ID")
private Long relationId;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "USER_ACCOUNT_USER_GROUP_ROLE_LINE", joinColumns = { #JoinColumn(name = "RELATION_ID") }, inverseJoinColumns = { #JoinColumn(name = "USER_ID") }, uniqueConstraints = #UniqueConstraint(columnNames = { "USER_ID", "RELATION_ID" }))
private List<UserAccount> userAccountList = new ArrayList<UserAccount>();
#ManyToOne
#JoinColumn(name = "USER_GROUP_ID")
private UserGroup userGroup;
#ManyToOne
#JoinColumn(name = "ROLE_ID")
private Role role;
}
#Entity
#Table(name = "USER_ACCOUNT")
public class UserAccount implements Serializable {
#Id
#Column(name = "USER_ID")
#GeneratedValue
private Long id;
.....
#ManyToMany(mappedBy = "userAccountList", cascade = CascadeType.ALL)
private List<UserGroupRoleRelation> rolesInGroup = new ArrayList<UserGroupRoleRelation>();
}
I wanna find usergroups of a useraccount and i prepared a method with criteria. its like;
#Override
#Transactional
public List<UserGroup> findUserGroupOf(UserAccount userAccount) {
Criteria criteria = getSession().createCriteria(UserGroup.class);
criteria.createAlias("userAccountsRole", "userAccountsRole");
criteria.add(Restrictions.eq("userAccountsRole.userAccountList", userAccount));
return criteria.list();
}
But when i try to get result of that method, DB2 gives to me DB2 SQL Error: SQLCODE=-313, SQLSTATE=07004, SQLERRMC=null, DRIVER=3.63.75
Probably its about creating alias on many to many relation. I dont know what should i do to create alias on many to many. How can I get result of that function?
Thank
#Override
#Transactional
public List<UserGroup> findUserGroupOf(UserAccount userAccount) {
Criteria criteria = getSession().createCriteria(UserGroup.class);
criteria.createAlias("userAccountsRole", "userAccountsRole");
criteria.createAlias("userAccountsRole.userAccountList", "userAccountList");
criteria.add(Restrictions.eq("userAccountList.id", userAccount.getId()));
return criteria.list();
}
It works for me. I mean criteria on "id". But I don't understand why I cant check equality on object instead of id when there is ManyToMany list
It is not of creating alias. You are passing an object to hibernate on which it can not make any criteria. You need to create bidirectional mapping for that.Or else if you your requirement is just to fetch the the list of UserAccountList of particular UserGroup class you can follow the below code.
#Override
#Transactional
public List<UserGroup> findUserGroupOf(long userGroupId) {
Criteria criteria = getSession().createCriteria(UserGroup.class);
criteria.add(Restrictions.eq("id",userGroupId));
criteria.createAlias("userAccountsRole", "uar");
criteria.setFetchMode("uar.userAccountList",FetchMode.JOIN);
return criteria.list();
}

Categories