I am trying to map owner(User) with an offer(car) whenever user is adding an offer. With setup like below user_id is always null. What am i missing ? I think it's something with controller. Mappings seems ok to me but I may be wrong. Owner is the person who is logged in and adds an offer, so it should be mapped by his id.
Edit I have found out that my @AuthenticationPrincipal Authentication auth is always null anyways, even tho only authenticated user can acces POST /api/cars endpoint. It seems counter-intuitive
Car Controller
@Controller
public class CarController {
@Autowired
private CarService carService;
@Autowired
UserRepository userRepository;
@GetMapping("/api/cars")
public String getCars(Model model) {
model.addAttribute("cars", carService.getAllCars());
return "cars";
}
@GetMapping("/api/cars/new")
public String createNewCarOfferForm(Model model) {
Car car = new Car();
model.addAttribute("car", car);
return "createNewOfferForm";
}
@PostMapping("/api/cars")
public String saveCar(@ModelAttribute("car") Car car, @AuthenticationPrincipal Authentication auth) {
User customUser = (User)auth;
car.setOwner(customUser);
carService.saveCar(car);
return "redirect:/api/cars";
}
@GetMapping("/api/cars/view/{id}")
public String viewOffer(@PathVariable Long id, Model model) {
model.addAttribute("car", carService.getCarById(id));
return "viewOffer";
}
}
User Entity
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private String roles = "ROLE_USER";
@OneToMany(mappedBy = "owner")
private Set<Car> cars = new HashSet<>();
public User() {
super();
}
public User(String username, String password, String roles) {
super();
this.username = username;
this.password = password;
this.roles = roles;
//GETTERS SETTERS
Car entity
@Entity
public class Car {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String carPhotoUrl;
private String carTitle;
private double price;
private int yearModel;
private int mileage;
private String engineType;
private float engineCapacity;
private int enginePower;
private String gearboxType;
private String driveType;
private String colour;
private Boolean isDamaged;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name="user_id", referencedColumnName = "id")
private User owner;
public Car() {
super();
}
public Car(String carPhotoUrl, String carTitle, double price, int yearModel, int mileage, String engineType,
float engineCapacity, int enginePower, String gearboxType, String driveType, String colour,
Boolean isDamaged) {
super();
this.carPhotoUrl = carPhotoUrl;
this.carTitle = carTitle;
this.price = price;
this.yearModel = yearModel;
this.mileage = mileage;
this.engineType = engineType;
this.engineCapacity = engineCapacity;
this.enginePower = enginePower;
this.gearboxType = gearboxType;
this.driveType = driveType;
this.colour = colour;
this.isDamaged = isDamaged;
}
//GETTERS SETTERS
USER REPOSITORY
public interface UserRepository extends JpaRepository<User, Long>
{
Optional<User> findByUsername(String username);
Optional<User> findById(Long id);
}
User Details Service
public class UserDetailsService implements UserDetails{
private String username;
private String password;
private List<GrantedAuthority> authorities;
public UserDetailsService() {
super();
}
public UserDetailsService(User user){
this.username=user.getUsername();
this.password=user.getPassword();
this.authorities = Arrays.stream(user.getRoles().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
CodePudding user response:
I believe you need to mention cascade type in owner “cascade=CascadeType.ALL” and it essentially means that any change happened on User(owner)must cascade to Car as well.
If we save an User(owner), then all associated Cars will also be saved into the database. If you delete an User(owner) then all Cars associated with that User(owner) also be deleted. Simple enough !
CodePudding user response:
I fixed it but it took me some time. This is now endpoint to add a Car (so no @AuthenticationPrincipal)
@PostMapping("/api/cars/addCar")
public String saveCar(@ModelAttribute("car") Car car, Principal
principal) {
User user = userService.getUserByName(principal.getName());
car.setOwner(user);
carService.saveCar(car);
return "redirect:/api/cars";
}
I bet it's not a clean solution. I've changed return type of UserRepository from Optional to User. I think it will bring more problems in the future. But for now it works.