1

I'm fairly new to Spring/JPA so this is somewhat a trivial question.

I have two entities with a many-to-one relationship: Item and ItemType. Basically, ItemType simply represents a unique name for a set of Items. I use a CrudRepository<Item, Long> to store them. The relevant code is as follows (getters/setters/equals()/hashCode() omitted):

@Entity
public class Item { 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinColumn(name = "type_id")
    private ItemType itemType;

    public Item() {}

    public Item(ItemType itemType) {
        this.itemType = itemType;
    }
}


@Entity
public class ItemType {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column(unique = true, nullable = false)
    private String name;

    public ItemType() {}

    public ItemType(String name) {
        this.name = name;
    }
}

@Controller
public class ItemsController {
    @Autowired private ItemsRepo itemsRepo;

    @RequestMapping(value = "/item", method = RequestMethod.POST)
    @ResponseBody
    public Item addQuestionSet(@RequestBody Item item) {
        return itemsRepo.save(item);
    }
}

When I insert a new Item into the database, I want it to get a type_id from either an ItemType with the given name if it already exists, or from a newly persisted ItemType otherwise.

As of now, I naturally get an exception when trying to insert the second item with the same type:

org.hsqldb.HsqlException: integrity constraint violation: unique constraint or index violation

I could probably make a boilerplate check in my controller before saving a new item into repository. But this task is rather generic, I'm pretty sure there must be a convenient solution in JPA.

Thanks.

SqueezyMo
  • 1,606
  • 2
  • 21
  • 34

2 Answers2

0

It seems you are persist() method on the Item object rather than merge() method. I hope it will resolve your query.

DeepInJava
  • 1,871
  • 3
  • 16
  • 31
  • Thanks for the response. I never call any of these explicitly, I save the object with the `save()` method of CrudRepository. Shouldn't the save method be somehow derived from the schema? If I remove `CascadeType.PERSIST`, I get `org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing` – SqueezyMo Oct 18 '14 at 20:17
  • But I don't have one, my understanding is that Spring does it under the hood, based on the schema. http://stackoverflow.com/a/23578842/1815052 – SqueezyMo Oct 18 '14 at 20:51
0

I can see that the problem is when you "persist", try with "lazy" type. You could get the data only when you need it and EAGER always. I can give you an example how i do it

this is my class "CentroEstudio"

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   @Column(name = "idCentroEstudio",nullable=false)
   private Long idCentroEstudio;
   @ManyToOne(fetch = FetchType.EAGER,cascade=CascadeType.ALL)
   @JoinColumn(name = "idTipoCentroEstudio", nullable = false)       
   private TipoCentroEstudio tipoCentroEstudio;
   @Column(name="nombre",nullable=false)
   private String nombre;
   @Column(name="activo",nullable=false)
   private boolean activo;

this is my class "TipoCentroEstudio"

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   @Column(name="idTipoCentroEstudio",nullable=false)
   private Long idTipoCentroEstudio;       
   @Column(name="descripcion",nullable=false)
   private String descripcion;
   @OneToMany(fetch = FetchType.LAZY, mappedBy = "tipoCentroEstudio")
   private Set<CentroEstudio> centroEstudio = new HashSet<CentroEstudio>(0);

I'm sorry for the Spanish in the example, but I'm peruvian and I speak Spanish. I hope this helps you ...