Oleg Ushakov Oleg Ushakov - 21 days ago 6
Java Question

How to correctly bind form to @ManyToOne structure and save o DB. Spring MVC, Hibernate

I'm a novice java developer and now develop User Management application using Spring-Hibernate. I have two entities User and Email. And User entity has a field Email which is mapped to Email entity as @ManyToOne. Any Email can be used by multiple users.

When I save a new User in DB for every new user I get a new row Email, even if the same record is already in the Email Table. How to properly make save operation to avoid duplication of the same records in the table Email?

User.java

@Entity
@Table(name = "USER")
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="ID")
private Long id;
@Column(name = "name")
private String name;
@ManyToOne
@JoinColumn(name = "email_id")
private Email email;
public User(){
}
public Email getEmail() {
return email;
}
public void setEmail(Email email) {
this.email = email;
}
...
}


Email.java

@Entity
@Table(name = "EMAIL")
public class Email implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="ID")
private Long id;
@Column(name = "emailaddress")
private String emailaddress;
@OneToMany (mappedBy = "email", targetEntity=User.class)
private Set<User> user= new HashSet<User>();

public Email() {
}
public Email(String emailaddress) {
this.emailaddress = emailaddress;
}

public String getEmailaddress() {
return emailaddress;
}
public void setEmailaddress(String emailaddress) {
this.emailaddress = emailaddress;
}
...
}


Controller.java

@Transactional
@RequestMapping(value = "/adduser", method = RequestMethod.POST)
public String saveOrder(@ModelAttribute("user") User user, BindingResult result, @RequestParam String action){
emailDAO.create(user.getEmail());
userDAO.create(user);
return "index";
...
}


EmailDAO.java

@Transactional
@Repository
public class EmailDAO{
@Autowired
private SessionFactory sessionFactory;
public Email create(Email email) {
sessionFactory.getCurrentSession().save(email);
return email;
}
}


UserDAO.java

@Transactional
@Repository
public class UserDAO{
@Autowired
private SessionFactory sessionFactory;

public User create(User user) {
sessionFactory.getCurrentSession().save(user);
return user;
}
}


webform.jsp

<form:form action="${formUrl}" method="post" modelAttribute="user">
<form:label path="name" for="appname">username</form:label>
<form:input path="name" id= "appname" cssClass="form-control"/>

<form:label path="email.emailaddress" for="appemail">Email</form:label>
<form:input path="email.emailaddress" id= "appemail"/>
<button type="submit" name="action" value="Add">Save</button>
</form:form>


database diagram

Database diagram

Example of the DB records

enter image description here
enter image description here

Answer

That is because you keep on saving the Email as a new Record

@Transactional
@RequestMapping(value = "/adduser", method = RequestMethod.POST)
    public String saveOrder(@ModelAttribute("user") User user, BindingResult result, @RequestParam String action){
    emailDAO.create(user.getEmail()); // Inserting Email as New Record
    userDAO.create(user);
    return "index";
...
}

And you don't have unique=true on Email Entity

@Column(name = "emailaddress", unique = true)
private String emailaddress;

Which you should ideally have so that there will be no duplicate Emails will get inserted even by accidentally.

You need to modify EmailDAO

@Transactional
@Repository
public class EmailDAO{
    @Autowired
    private SessionFactory sessionFactory;

    public Email create(Email email) {
        sessionFactory.getCurrentSession().save(email);
        return email;
    }

    public Email getEmail(String inputEmail) {
        Email email = null;
        Query query = sessionFactory.getCurrentSession().createQuery("FROM Email e WHERE e.emailAddress = :email");
        query.setString("email", inputEmail);
        List emails = query.list();
        if(emails != null && emails.size() > 0) {
            email = emails.get(0);
        } else {
            email = new Email();
            email.setEmailAddress(inputEmail);
        }
        return email;
    }
}

And you getEmail

@Transactional
@RequestMapping(value = "/adduser", method = RequestMethod.POST)
    public String saveOrder(@ModelAttribute("user") User user, BindingResult result, @RequestParam String action){
    user.setEmail(emailDAO.getEmail(user.getEmail().getEmailAddress())); // Inserting Email as New Record
    userDAO.create(user);
    return "index";
...
}