0
votes

I have two entities Role and Grant with the following mapping:

public class Role extends BaseBean {
    private static final long            serialVersionUID   = 1L;
    private String                     name;
    private Set<Grant> grants = new HashSet<Grant>();
// get set
}

public class Grant implements Serializable {
    private static final long   serialVersionUID    = 1L;
    private String            id;
    private String            data;
}

mapping orm:

 <entity name="q2role" class="tn.waycon.alquasar2.adm.model.Role">
        <attributes>
            <basic name="name">
                <column length="800" nullable="false" unique="true"/>
            </basic>
            <many-to-many name="grants" fetch="EAGER">
                <join-table name="role_grant">
                    <join-column name="role_id"/>
                    <inverse-join-column name="grant_id"/>
                </join-table>
                <cascade>
                    <cascade-all/>
                </cascade>
            </many-to-many>
        </attributes>
    </entity>

<entity name="q2grant" class="tn.waycon.alquasar2.adm.model.Grant">
        <attributes>
            <id name="id">
                <column name="id_g"/>
                <generated-value stategy="IDENTITY" generator="SEQ_GEN"/>
            </id>
            <basic name="data"></basic>
        </attributes>
</entity>

Now when i try to insert a new Role that contains an existing grants the transaction will fail because eclipselink is trying to insert the grants that already exist. Why eclipselink is doing this strange behaviour ? I am setting cascade-all and eclipselink must be smart enough to separate between cascade-persist and cascade-merge.

Main {
Role role = new Role();
List<Grant> grants = grantRepository.getGrantsBydata(List<String> datas);
role.setGrants(grants);
roleRepository.save(role);
}

The log:

WARNING [http-nio-8080-exec-2] org.springframework.remoting.support.RemoteInvocationTraceInterceptor.invoke Processing of HttpInvokerServiceExporter remote call resulted in fatal exception tn.waycon.alquasar2.adm.service.api.IAdminService.createRole   org.springframework.transaction.TransactionSystemException: Could not commit transaction JPA; nested exception is javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd) org.eclipse.persistence.exceptions.DatabaseException Internal Exception: java.sql.BatchUpdateException: Violation of PRIMARY KEY "PK__Q2GRANT__9DB7D2FA15DA3E5D". Can not insert duplicate key in object 'dbo.Q2GRANT ". duplicate key value (13969). Error Code: 2627

2
Is your save method using merge or persist?Chris
I am using spring data jpa, there is only one save method in the JpaRepository interface ,depending on the primary key null or not it will decide to use merge or persist.SEY_91

2 Answers

1
votes

Calling Persist causes JPA to insert the root entity, but it also cascades the persist call across the relationship marked with the cascade persist type. This indirectly means you are calling persist on your detached entity, which the JPA specification requires providers to throw an exception either immediately or when the transaction synchronizes with the database (the insert statement).

Persist and cascade persist options are only meant to be used when you are going to be inserting a new object graph, so you might want to reevaluate where you put in the cascade persist option - it has consequences.

Options are

  1. Reading in existing entities before adding them to the new entity and using the managed instance. Because persist on a managed entity is a no-op, this will resolve your issue.
  2. Use merge instead. Merge allows the provider to look at the entity instance to decide if it is new or an update, and this cascades to the graph as specified. Merge will then take your detached entity and handle it appropriately by updating it.
0
votes

You should remove as Grants are managed separately from Roles. Using means that when a Role is inserted, all its Grants are also inserted, and when it is deleted, the Grants are deleted too. In you case I can see that the Grants already exist when the Role is created, so you will not want them for example to be deleted when that Role will be deleted!