How to avoid creating a new row if similar row exists?

user2071377 picture user2071377 · Jun 13, 2013 · Viewed 15.8k times · Source

I need to configure hibernate to avoid creating duplicate rows, (although the row exists it creates a new one, and since only one filed is set it set all the rest to NULL)

Lets say I have a row as following

id des    index age
1  MyName 2     23

Although I just set MyName as des and it already exists in the Name table hibernate create a new row as following

id des    index age
1  MyName 2     23
2  MyName Null  Null     << new row with null values will be created 
                            rather than updating the previous one

When I want to sa . So I have added the following annotation to my class but it crossed the Entity, and dynamicUpdate.

@org.hibernate.annotations.Entity(
  dynamicUpdate = true
)

I used @DynamicUpdate as well, although hibernate accept it but still I have the same problem.

Is there any other method to do it ? Version of my hibernate is as following

<dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>4.2.1.Final</version>
      <type>jar</type>
</dependency>

*Based on Ray's comment blew, By assigning a value to Id of child class it works properly but how about if I do not have the ID? do I have to do a select to find the id first ? Is there any method to force hibernate to do it automatically r based on values of child class rahter than doing a separate select to find the id? *

User.Java

....
import org.hibernate.annotations.DynamicUpdate;

@Entity
@Table(name = "user") 
@DynamicUpdate
public class User implements Serializable {

  private int ID;
  private Name name;
  private String type;

  public User() {
  }

  @Id
  @GeneratedValue
  @Column(name = "id")
  public int getID() {
     return ID;
  }

  public void setID(int ID) {
     this.ID = ID;
  }

  @ManyToOne(cascade = CascadeType.ALL)
  public Name getName() {
     return name;
  }

  public void setName(Name name) {
    this.name = name;
  }

  .....

Name.Java

@Entity()
@Table(name = "Name")
public class Name implements Serializable {

private int id;
private String des;
private String index;
private String age;

public Name() {
}

@Id
@GeneratedValue
@Column(name="id", unique= true, nullable = false)
public int getId() {
    return id;
}

public void setId(int id) {
    this.id = id;
}

.....

Model.java

public void addMyUsers(){
   Name name = new Name();
   name.setDes("MyName");
   While( ..... )
   {
       User user = new User();
       user.setName(name);
       user.setType(X);
       addUser(user);
   }
}

public void addUser(User user) {
        session = Util.getSession();

        session.beginTransaction();


        session.merge(user); 
        //session.saveOrUpdate(user);

        session.getTransaction().commit();

        session.close();
}

Answer

Glen Best picture Glen Best · Jun 28, 2013

although the row exists it creates a new one

That's not quite right. It's not just hibernate automatically deciding to create a new user. Hibernate is doing what your code tells it to:

  • In addMyUsers(), you create new Name() and new User(), and you give neither of these a pre-existing ID. You have made these objects look like new ones, rather than pre-existing ones to be updated.
  • In addMyUser(), you call session.merge(user). Hibernate sees the objects have no ID - so it merges them and assigns them the NEW status. When the transaction is flushed & committed, hibernate generates SQL to create new IDs and store the objects as new records.

If you want to determine whether an object is pre-existing, and operate on the previous record where possible:

  1. Obtain the fields that you want to use (e.g. from a web form) - in your case, this includes "det" field.
  2. See if the record already exists in the database. Retrieve the object using hibernate via session.find() or session.get(), in your case using the "det" field.
  3. If the object cannot be found, then create a new object.
  4. For retrieved objects, you can optionally detach the object from the session before modification, (e.g. via session.clear()). Newly created objects are already in a detached state.
  5. Modify the object (set it's fields).
  6. If the object is detached, then attach via session.merge(). Merge works for both pre-existing detached objects (obtained via retrieval) and new detached objects (via new <Object>()). Alternatively, for pre-existing detached objects you can call session.update() and for new detached objects you can call session.save()/persist().
  7. flush/commit the transaction.

You're missing (2).