Spring Data JPA persistence - Could not commit JPA transaction - ORA-00001: unique constraint violated

不问归期 提交于 2019-12-12 03:26:53

问题


I am trying to save an entity that has a many-to-many association to another entity and cascade the persistence to the associated entity and create the association using spring data jpa repository.

I can insert the parent entity_a which contains a set of entity_b using entityARepository.save(entityA). Spring jpa is taking care of all the inserts needed in the transaction. All the entity_b's get inserted, entity_a's get inserted and the join table in the middle has the association inserted as well. If I update the same entity_a with a new value in, say timestamp column, the same entityARepository.save(entityA) handles this and does a corresponding update.

The problem happens when there already exists entity_b (which has an association between some entity_a) and I try to insert a new entity_a with the same entity_b. It is many to many so this is how the data model is supposed to be. But instead of updating the existing entity_b during this entityA save() transaction, it tries to do inserts on entity_b and a constraint violation exception on the primary key is thrown.

org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (USER1.SYS_C0013494) violated

Error Code: 1
Call: INSERT INTO ENTITY_B (ID, NAME, VALUE, TIME_STAMP) VALUES (?, ?, ?, ?)
    bind => [4 parameters bound]
Query: InsertObjectQuery(EntityB [name=shape, value=circle])

The problem is that spring doesn't have update(). It only has save which should handle update if it receives the same primary key. It's not doing that when a new entity_a is saved and has a collection of entity_b, if any entity_b exists, the whole transaction is failing sure to primary key constraint violation of entity_b.

public class EntityA {

    @Id
    @SequenceGenerator( name = "EntityASeq", sequenceName = "SQ_ENTITY_A", allocationSize = 1, initialValue = 1 )
    @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "EntityASeq")
    @Column(name = "ID")
    private Integer id;

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
    @JoinTable(name = "MY_JOINED_TABLE", 
    joinColumns = {
        @JoinColumn(name = "a_id", referencedColumnName = "ID")}, 
    inverseJoinColumns = {
        @JoinColumn(name = "b_id", referencedColumnName = "ID")})
    private Set<EntityB> attributes;

    // These three columns below have a unique constraint together.
    @Column(name = "name")
    private String name;
    @Column(name = "tenant")
    private String tenant;
    @Column(name = "type")
    private String type;

    @Column(name = "timestamp")
    private Timestamp timestamp;

}

public class EntityB {

    @Id
    @SequenceGenerator( name = "EntityBSeq", sequenceName = "SQ_ENTITY_B", allocationSize = 1, initialValue = 1 )
    @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "EntityBSeq")
    @Column(name = "ID")
    private Integer id;

    @ManyToMany(mappedBy = "attributes")
    private Set<EntityA> aSet;

    // These two columns below have a unique constraint together.
    @Column(name = "name")
    private String name;
    @Column(name = "value")
    private String value;

    @Column(name = "timestamp")
    private Timestamp timestamp;

}

The id for each is generated by default. I also have a unique constraint on a few columns, which means if an EntityB has the same name/value as an existing one in the database, I want to just update the timestamp. That works if entity_a is already in the table and it has the same entity_b's. A and B's timestamp are updated and no error when I persist with entityARepository.save(entityA). (I do some checking on the db with findOne because the id is auto generated an not known. So if a name/value exist, I don't try to insert with a new id, I use the same one in the db and it works (similarly with entity_atenant/name/type.

It also works when I persist an existing entity_a with updated entity_b's. So if a new entity_b is associated with entity_a (that exists as an association with a different entity_a), etc, that works and the persistence is working.

The issue again, is just on INSERT of entityA via repo.save() when some entity_b s already exist for other associations. It should be doing:

INSERT INTO entity_a ...
UPDATE entity_b ...
INSERT INTO MY_JOINED_TABLE ...

But it seems like it's doing

INSERT INTO entity_a ...
INSERT INTO entity_b ... -- fails because primary key constraint fails
INSERT INTO MY_JOINED_TABLE ...

EDIT: I tried removing CascadeType.PERSIST but I get an error saying

During synchronization a new object was found through a relationship that was not marked cascade PERSIST: EntityB [name=color, value=blue].

I wanted to try to manually insert/update but I couldn't do that. It wants me to have the EntityA specified with PERSIST because it has associations to the entityB

I tried inserting in the reverse and now I'm having issues inserting from entityB.save() when there already exists some entityA and I'm adding a new entityA to entityB

来源:https://stackoverflow.com/questions/36225540/spring-data-jpa-persistence-could-not-commit-jpa-transaction-ora-00001-uniq

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!