Spring data unusual query - java

i have a problem with spring data queries. I want to receive Users (List) where user has given role ( role is in a list ) and assignedOrders of this user (list) is empty or this orders are in given state (State is enum class).
I came up with such query:
List<User> findAllByRoleContainsAndOrdersAssignedStateIsNullOrOrdersAssignedStateEquals(State state,Role role);
but it do not work at all. It is complicated and i've never created such query before. Can you help me with that? Thanks a lot !
EDIT:
Piece of code :
#Entity
public class User implements Persistable {
...........
#Column(name = "ROLES")
#Enumerated(EnumType.STRING)
#ElementCollection(targetClass = Role.class)
private List<Role> role;
..........
#OneToMany(fetch = FetchType.LAZY, mappedBy = "driver")
private List<Order> ordersAssigned = new ArrayList<>();
}
#Entity
public class Order implements Persistable {
...........
#JoinColumn(name = "DRIVER_ID")
#ManyToOne(fetch = FetchType.LAZY,cascade = CascadeType.PERSIST)
private User driver;
#Column(name = "STATE")
#Enumerated(EnumType.STRING)
private State state;
.............
}

Try something like this:
#Entity
public class User {
private String name;
#ElementCollection
#Column(name = "role")
private List<Role> roles = new ArrayList<>();
#OneToMany(mappedBy = "user")
private List<Order> orders = new ArrayList<>();
public User(String name, Role... roles) {
this.name = name;
this.roles.addAll(asList(roles));
}
public enum Role {
ROLE1, ROLE2
}
}
#Entity
public class Order {
private String title;
#ManyToOne
private User user;
private State state;
public enum State {
STATE1, STATE2
}
}
public interface UserRepository extends JpaRepository<User, Long> {
#Query("select distinct u from User u join u.orders o join u.roles r where r = ?1 and (o.state is null or o.state = ?2)")
List<User> getUsers(User.Role role, Order.State state);
}
Working example.
More info is here and here.

Related

Spring JPA query returns multiple copies of the same entity

I have two entities in my project User and Order, they are linked as ManyToOne, when I'm trying to get all Users with specific Order status I get multiple copies of the same User. What am I doing wrong here?
User.java
#Getter
#Setter
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "t_user")
public class User {
#Id
private UUID id;
#OneToMany(mappedBy = "user")
Set<Order> orders = new HashSet<>();
}
Order.java
public class Order {
#Id
private UUID id;
#Column(name = "order_status", nullable = false)
OrderStatus orderStatus;
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
public void addUser(User user) {
this.user = user;
user.getOrders().add(this);
}
public void removeUser() {
if (user != null) {
user.getOrders().remove(this);
this.user = null;
}
}
}
OrderStatus.java
public enum OrderStatus {
PAYMENT, PAID, CAR_IN_USE, CAR_RETURNED, CLOSED;
}
UserDao.java
#Repository
public interface UserDao extends JpaRepository<User, UUID> {
Page<User> findAllByOrders_OrderStatus(OrderStatus orderStatus, Pageable pageable);
}
findAllByOrders_OrderStatus always return multiple copies of User (if I have 6 orders, I'll have 6 User copies). What should I change here?

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())
}

How to persist nested objects with Hibernate?

I have two classes, Customer and ShoppingCart. The java structure of the two classes is the following:
Customer class:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class Customer extends User implements Serializable {
#OneToOne(mappedBy = "owner", cascade=CascadeType.ALL)
private ShoppingCart shoppingCart;
#OneToMany(mappedBy = "customer", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Purchase> purchases;
public Customer() {
super();
}
public Customer(String username, String email, String password) {
super(username, email, password);
this.shoppingCart = new ShoppingCart();
this.purchases = new ArrayList<>();
}
getters and setters
}
ShoppingCart class:
#Entity
public class ShoppingCart implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer shoppingCartId;
#OneToOne
#JoinColumn(name = "owner_id")
private Customer owner;
#OneToMany(mappedBy = "shoppingCart")
private List<CartItem> items;
public ShoppingCart(Customer owner) {
this.owner = owner;
this.items = new ArrayList<>();
}
public ShoppingCart() {
this.items = new ArrayList<>();
}
getters and setters
}
If needed, this is the User class:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer userId;
private String username;
private String email;
private String password;
public User() {
}
public User(String username, String email, String password) {
this.username = username;
this.email = email;
this.password = password;
}
getters and setters
}
I have configured the Repositories classes in this way:
#Repository
public interface CustomerRepository extends CrudRepository<Customer, Integer> {
}
#Repository
public interface UserRepository extends CrudRepository<User, Integer> {
}
#Repository
public interface ShoppingCartRepository extends CrudRepository<ShoppingCart, Integer> {
}
What I want is simple, once I create a Customer, I also want to create a ShoppingCart tuple inside the database. And it actually works fine, the only problem is that the foreign key of the ShoppingCart related with the Customer is set to null. I just have the shopping_cart_id attribute set to an integer value (correctly).
The code I used to test it is the following:
Customer customer = new Customer("stefanosambruna", "ste23s#hotmail.it", "*******");
customerRepository.save(customer);
Now, I may have put some annotations in the wrong places, but I really don't know which ones. Is it related to the constructors? Or to the #JoinColumn and mappedBy configurations? I read all the Q&As about this topic here on StackOverflow and on some other sources, but I didn't find anything 100% useful. Hope to have given all the necessary details.

Query dsl with predicate and fetch join

is there a chance to fetch join entity with using predicate?
#Entity
public class Student {
#Id
private int id;
private String hobby;
private String favouriteColor;
private String name;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
#JoinColumn(name = "CLASS_ID", referencedColumnName = "ID")
private Classroom classroom;
}
#Entity
public class Classroom {
#Id
private int id;
private String classRoom;
private List<Student> students;
}
I have some predicate
public class StudentPredicate {
private StudentPredicate() {}
public static Predicate createPredicate(final Student filter) {
QStudent student = QStudent.student;
BooleanBuilder builder = new BooleanBuilder();
if (isNotBlank(filter.getHobby())) {
builder.and(student.hobby.eq(filter.getHobby());
}
if (isNotBlank(filter.getFavouriteColor())) {
builder.and(student.favouriteColor.eq(filter.getFavouriteColor()));
}
return builder.getValue();
}
}
and repository
#Repository
public interface StudentRepository extends CrudRepository<Student, Integer>, QueryDslPredicateExecutor<Student> {
}
and now how can I find all students with fetched classrooms?
studentRepository.findAll(predicate)
How to say to query dsl that these findAll should fetch classroom?
As there's FetchType.LAZY for classroom field in Student class, so here while you call getClassRoom() will actually fetch the related entity from db or either you can use FetchType.EAGER.

MVC SpringBoot and JPA Relationship

I have an issue with the JPA relationship within a MVC SpringBoot application.
I'm trying to create an application where I have a number of users and every user can have a number of cars. A user can have multiple cars. I've done a #OneToOne relationship between the user and the car object, here's what I've done:
here is the User class:
#Entity
#Table(name = "user")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "username", nullable = false)
private String username;
#Column(name = "password", length = 500, nullable = false)
private String password;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Car> cars;
}
then here is the Car class:
#Entity
#Table(name = "car")
public class Car implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(length = 11)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "make", nullable = false)
private String make;
#Column(name = "model", nullable = false)
private String model;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "id")
private User user;
}
Then, here is the actual service implementation
#Component
#Transactional(readOnly = true)
public class CarServiceImpl implements CarService {
#Inject
private CarRepository carRepository;
#Inject
private UserRepository userRepository;
#Override
#Transactional(readOnly = false)
public Car addCar(Long userId, Car car) {
User user = userRepository.findOne(userId);
user.getGpsLocationModels().add(car);
car.setUser(user);
carRepository.save(car);
return car;
}
then I have the endpoint but that works fully. The add method looks like does work as supposed to, at least I get the expected output, however the find method I have no idea how to write it, well can't figure it out how to retrieve cars based on user, I know how to get them by their ID but not for every user separately.
Here is my try:
#Override
public Car findCar(Long userId, Long carId) {
//get the current user (that comes as JSON Request Param)
User user = userRepository.findOne(userId);
//get the car based on its ID, here's the problem, I want the car based on its ID AND its user, I can't display cars which aren't for that particular user
Car car = carRepository.findOne(carId);
return car;
}
Here is the method for get all cars for a particular user:
#Override
public List<Car> displayAllCars(Long userId) {
return userRepository.findOne(userId).getCars();
}
I'd really appreciate any help that you could advise.
Thank you so much
Your mappings are incorrect. Car > User is #ManyToOne. If you also make this bi-directional you can also then retrieve the cars via the user:
#Entity
#Table(name = "user")
public class User implements Serializable {
#OneToMany(mappedBy ="user",cascade = CascadeType.ALL)
private Set<Car> cars;
public Set<Car> getCars(){
return cars;
}
public void addCar(Car car){
cars.add(car);
car.setUser(this);
}
}
#Entity
#Table(name = "car")
public class Car implements Serializable {
#ManyToOne(fetch=FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name="user_id")
private User user;
}
#Override
public Set<Car> findCars(Long userId) {
return userRepository.findOne(userId).getCars();
}
You could have a method that accepts the user ID and returns the list in the car repository like:
List<Car> findCarByUser(Long userID);
And then you will have
#Override
public List<Car> displayAllCars(Long userId) {
return carRepository.findCarByUser(userId);
}

Categories