0

I have a 3 different Entity classes ie Pashmina, Description, Image, PashminaColour. Here Pashmina have a one-to-many relationship with Description, Image, and PashminaColour. I am trying to save all these entities at a same time but got some error there:

(org.hibernate.HibernateException) org.hibernate.HibernateException: org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session : [com.nepitc.mshandloomfrabics.entity.Description#0]

I have used the following code to save

@Override
public void insert(T t) throws HibernateException {
    session = sessionFactory.openSession();
    trans = session.beginTransaction();

    try {
        session.save(t);
        trans.commit();
    } catch(HibernateException ex) {
        trans.rollback();
        throw new HibernateException(ex);
    } finally {
        session.close();   
    }
}

Note: If I save Pashmina details with only one image, description or pashmina colour it let me insert but if I save Pashmina with multiple images, pashmina colours or description shows me an error.

This is how I have implemented a controller

@RequestMapping(value = "/add-pashmina", method = RequestMethod.POST)
    public @Async ResponseEntity<String> insertPashmina(@RequestBody Pashmina pashmina) {
        if (pashmina != null) {
            try {
                pashminaService.insert(pashmina);

                pashminaId = pashmina.getPashminaId();

                for (PashminaColour pash : pashmina.getPashminaColor()) {
                    pashminaColorService.insert(new PashminaColour(pash.getColor(), new Pashmina(pashminaId)));
                }

                for (Description desc : pashmina.getDescriptions()) {
                    descriptionService.insert(new Description(desc.getPashminaDescription(), new Pashmina(pashminaId)));
                }

                return new ResponseEntity<>(HttpStatus.OK);

            } catch (HibernateException e) {
                return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
            }
        } else {
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        }
    }

Pashmina

public class Pashmina implements Serializable {

    private static final long serialVersionUID = 1L;
    // @Max(value=?)  @Min(value=?)//if you know range of your decimal fields consider using these annotations to enforce field validation
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "sq_pashmina_id")
    @SequenceGenerator(name = "sq_pashmina_id", sequenceName = "sq_pashmina_id")
    @Column(name = "PASHMINA_ID", unique = true, nullable = false)
    private int pashminaId;

    @Column(name = "PASHMINA_NAME")
    private String pashminaName;

    @Column(name = "PRICE")
    private double price;

    @Column(name = "ADDED_AT", insertable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date addedAt;

    @Column(name = "CATEGORY")
    private String category;

    @Column(name = "ENABLED", insertable = false)
    private Character enabled;

    @OneToMany(mappedBy = "pashmina", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private List<PashminaColour> pashminaColor;

    @OneToMany(mappedBy = "pashmina", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private List<Image> images;

    @OneToMany(mappedBy = "pashmina", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private List<Description> descriptions;

Image

public class Image implements Serializable {

    private static final long serialVersionUID = 1L;
    // @Max(value=?)  @Min(value=?)//if you know range of your decimal fields consider using these annotations to enforce field validation
    @Id

    @Column(name = "IMAGE_ID")
    private int imageId;

    @Column(name = "IMAGE_NAME")
    private String imageName;

    @JoinColumn(name = "PASHMINA_ID", referencedColumnName = "PASHMINA_ID")
    @ManyToOne
    private Pashmina pashmina;

    @Column(name = "PUBLIC_ID")
    private String publicId;

PashminaColour

public class PashminaColour implements Serializable {

    private static final long serialVersionUID = 1L;
    // @Max(value=?)  @Min(value=?)//if you know range of your decimal fields consider using these annotations to enforce field validation
    @Id

    @Column(name = "COLOUR_ID", insertable = false)
    private int colourId;
    @Column(name = "COLOR")
    private String color;

    @JoinColumn(name = "PASHMINA_ID", referencedColumnName = "PASHMINA_ID")
    @ManyToOne
    private Pashmina pashmina;

Description

public class Description implements Serializable {

    private static final long serialVersionUID = 1L;
    // @Max(value=?)  @Min(value=?)//if you know range of your decimal fields consider using these annotations to enforce field validation
    @Id

    @Column(name = "DESCRIPTION_ID")
    private int descriptionId;

    @Column(name = "PASHMINA_DESCRIPTION")
    private String pashminaDescription;

    @JoinColumn(name = "PASHMINA_ID", referencedColumnName = "PASHMINA_ID")
    @ManyToOne
    private Pashmina pashmina;

For each of the class Id is inserted using trigger in oracle database. Thanks!

Let me know if you guys don't understand me

This is how I have send a entity to controller enter image description here

Chris Hadfield
  • 494
  • 1
  • 7
  • 34
  • You should not do 3 inserts for inserting a Pashmina with its Color and Description. What you should do is create your Pashmina with Color & Description and then call insert(). The cascades in Pashmina will make sure that all three are persisted correctly, and you will not get the "nonunique" error. In general, when you need to use an id to manually set in another entity, you are doing it wrong. – Kirinya Jun 28 '18 at 10:35
  • Can you provide me a sample or correct my above code – Chris Hadfield Jun 28 '18 at 10:39
  • Possible duplicate of [Hibernate Error: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session](https://stackoverflow.com/questions/1074081/hibernate-error-org-hibernate-nonuniqueobjectexception-a-different-object-with) – Vadzim May 16 '19 at 20:17

2 Answers2

2

I think problem is in mismatching your entity class name that you are sending from ajax request. I see that you are sending PashminaModel entity but you are using only Pashmina in your spring POJO class. Try chaning your entity class ie. Pashmina to PashminaModel, Description to DescriptionModel, Image to ImageModel, Pashmina to PashminaModel.

Hope it works.

Nishan Dhungana
  • 831
  • 3
  • 11
  • 30
0

Assuming your Pashmina sets the relationships correctly, you should only need:

    if (pashmina != null) {
        try {
            pashminaService.insert(pashmina);

            return new ResponseEntity<>(HttpStatus.OK);

        } catch (HibernateException e) {
            return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
        }
    } else {
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

I would, in any case, never use a Hibernate entity to send or receive from the client - you can never trust your client to not alter your data in ways that are harmful.

To set the relationships correctly, you need to set the relationship on both sides iff both sides have a mapping to the other side, like so:

public void addPashminaColour(PashminaColour color) {
    this.pashminaColour.add(color);
    color.setPashmina(this);
}

Alternative, with clear separation of model and entity:

    public @Async ResponseEntity<String> insertPashmina(@RequestBody PashminaModel pashminaModel) {
    if (pashmina != null) {
        try {

            PashminaModel pashmina = pashminaConverter.convert(pashminaModel);

            pashminaService.insert(pashmina);

            return new ResponseEntity<>(HttpStatus.OK);
            ...
        }
        ...
    }


public class PashminaConverter {

    public Pashmina convert(PashminaModel model) {
        Pashmina pashmina = new Pashmina();

        // copy primitive fields from model to entity

        for (ColorModel colorModel : model.getColors()) {
            pashmina.addColor(colorConverter.convert(colorModel);
        }

        // same for DescriptionModel
    }
}

public class ColorConverter {

    public PashminaColour convert(ColorModel model) {
        // copy primitive fields from model to entity
    }

}

public class Pashmina {

    ...

    public void addColor(PashminaColour color) {
        this.pashminaColour.add(color);
        color.setPashmina(this);
    }

    ...
}
Kirinya
  • 245
  • 3
  • 12
  • it only saves the pashmina but not other. I might have a relationship problem. Please have a look at my relationship I have defined above can you tell me in details how can I set a relationships between these entities – Chris Hadfield Jun 28 '18 at 11:30
  • How are the subentities set in your @Requestbody Pashmina? This is probably the problem, the mapping in itself seems ok (I would change List to Set, however). – Kirinya Jun 28 '18 at 11:49
  • I have added a image please look above – Chris Hadfield Jun 28 '18 at 12:04
  • I would change your Controller so that you do not send the entity Pashmina directly, but a POJO PashminaModel. After sending that model to your backend, convert it to the entity Pashmina and add the Colors and Descriptions like above. This has two advantages, (a) your application does not trust the client (which it should not do), and (b) your insert() method will work correctly. – Kirinya Jun 28 '18 at 15:16
  • Sorry I didn't get you would you please correct my above code and post – Chris Hadfield Jun 28 '18 at 16:13