Search code examples
javasqlspringspring-data-jpahibernate-mapping

@OneToOne mapping isnt working in my Java Spring project


So I am tidying up my small Spring project and I noticed for some reason the @OneToOne annotation is not doing its job for me which in turn causes issues in another model.

github link : https://github.com/eamonmckelvey/sports-app

Basically, I have a User model class, a team model class and a player model class. I want only one user to be able to create one team, and one team to have many players. However, I am able to add as many teams to my user as I want which is wrong.

All the answers provided require me to add a no arg constructor and a constructor for my users class, but when I do this I get an error in my registration from class.

Please help.

1. User Model

 @Entity
 @Data
 @NoArgsConstructor(access= AccessLevel.PRIVATE, force=true)
 @RequiredArgsConstructor
 public class User implements UserDetails {

    @OneToOne(cascade = CascadeType.ALL,mappedBy = "user")
    private Team team;

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    private Long id;
    private final String username;
    private final String password;
    //private final String fullname;




    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
    }
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return true;
       }
   }

2. Team Model

@Data
@Entity
 @Table(name="User_Team")

public class Team implements Serializable {

@OneToOne(fetch= FetchType.LAZY)

private User user;

private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy= GenerationType.AUTO)
private Long id;

//@NotBlank(message="Team Name is required")
private  String teamName;

//@NotBlank(message="Location is required")
private  String location;

//@NotBlank(message="Nickname required")
private String nickName;

private String yearEstablished;

public Sport sport;

private Divison divison;

3. Team Controller

@Slf4j
@Controller
@SessionAttributes("Team")

public class TeamController {

private TeamRepository teamRepository;

public TeamController(TeamRepository teamRepository) {
    this.teamRepository = teamRepository;
}

 @Autowired
 TeamRepository service;

 @GetMapping("/team")
 public String displayTeam(Model model) {

    model.addAttribute("team", service.findAll());

    return "/team";
   }

    @GetMapping("/addTeam")
    public String showSignUpForm(User user) {
    return "addTeam";
   }

    @PostMapping("/addTeam")
    public String processOrder(@Valid Team team, BindingResult result, SessionStatus 
    sessionStatus,
                           @AuthenticationPrincipal User user, Model model) {
    if (result.hasErrors()) {
        return "addTeam";
    }

    team.setUser(user);
    service.save(team);
    model.addAttribute("team", service.findAll());
    return "team";
}

4. Registeration Form

@Data
public class RegistrationForm {
private String username;
private String password;
//private String fullname;


 public User toUser(PasswordEncoder passwordEncoder) {
    return new User(
            username, passwordEncoder.encode(password));
}
}

5. Registration Controller

@Controller
@RequestMapping("/register")
public class RegistrationController {

private UserRepository userRepo;
private PasswordEncoder passwordEncoder;

public RegistrationController( UserRepository userRepo, 
 PasswordEncoder passwordEncoder){
    this.userRepo = userRepo;
    this.passwordEncoder = passwordEncoder;
}

 @GetMapping
 public String registerForm(){
    return "registration";
 }
  @PostMapping
 public String processRegistration(RegistrationForm form){
    userRepo.save(form.toUser(passwordEncoder));
    return "redirect:/login";
}

6. user details class

@Service
public class UserRepositoryUserDetailsService implements 
UserDetailsService {

private UserRepository userRepo;
@Autowired
public UserRepositoryUserDetailsService(UserRepository userRepo) {
    this.userRepo = userRepo;
}
@Override
public UserDetails loadUserByUsername(String username) throws 
UsernameNotFoundException {
    User user = userRepo.findByUsername(username);

    if (user != null) {
        return user;
    }
    throw new UsernameNotFoundException(
            "User '" + username + "' not found");
}

Solution

  • Hey so i found a fix here for my code.

    1. Team Controller

          @GetMapping("/addTeam")
          public String showSignUpForm(SessionStatus sessionStatus,
                                 @AuthenticationPrincipal User user, Model model) 
     {
          //if the user has already the team we should not let them add another 
          // one
          //this is due to having one to one relationship
         long userHasTeamCount = service.countAllByUser(user);
         if (userHasTeamCount > 0) {
    
            return "redirect:team";
    
         }
    
        return "addTeam";
      }
    

    2. Team model

       @OneToOne(fetch = FetchType.EAGER)
       @JoinColumn(name = "user_id", referencedColumnName = "id")
       private User user;
    

    3. User Model

    Removed the OneToOne here as its not needed

    4. Team repo

    @Repository
    public interface TeamRepository extends JpaRepository<Team, Long> {
    Team findAllById(Long id);
    
    
    long countAllByUser(final User user);
    

    }