prevent cascading for many-to-many relationship in spring jpa with hibernate

11 Views Asked by At

My problem is that I want to have a many-to-many relationship defined on an entity and use JPA repository to automatically fetch related entities, but I don't want JPA repository to ever save related entities (or records in join table) - I need to do that separately for good reasons.

It seems like Hibernate is saving records into join table even though I tried to disable cascading in every possible way.

Example:

My code looks as follows (I extracted only important parts):

A product can have multiple tags linked via join table product_has_tag, JPA loads tags just fine. All cascading is turned off to not ever try insert any tags or records into product_has_tag via product entity.

@Entity
public class Product {

    @ManyToMany(fetch = FetchType.EAGER, cascade = {})
    @JoinTable(
        name = "product_has_tag",
        joinColumns = @JoinColumn(
            name = "product_id",
            insertable = false,
            updatable = false
        ),
        inverseJoinColumns = @JoinColumn(
            name = "tag_id",
            insertable = false,
            updatable = false
        )
    )
    @Cascade(value = {})
    protected Set<Tag> tags = new HashSet<>();

}

Entity for join table is defined separately so I am able to insert records into product_has_tag table when I want and in a fashion I want (I need to set value of 'special'). Insertable and Updatable were set to false in a desperate attempt to prevent cascading:

@Entity
public class ProductHasTag {

    @Id
    @Column(name = "product_id", nullable = false, insertable = false, updatable = false)
    protected Long productId;

    @Id
    @Column(name = "tag_id", nullable = false, insertable = false, updatable = false)
    protected Long tagId;

    protected String special;

}

And now, in some business logic I want to insert products and then records to product_has_tag - those will be part of the same transaction, but I need them to be inserted manually and not via Hibernate cascading:


@Autowired
TagRepository tagRepository;

@Autowired
ProductRepository productRepository;

@Autowired
ProductHasTagRepository productHasTagRepository;


@Transactional
public void saveItDude(
) {

    // get a tag somehow and save it
    Tag tag = new Tag();
    tag.setName('some tag');

    tagRepository.save(tag); // this is ok

    // get a product somehow
    Product product = productRepository.findById(1); 
    product.setSomething('hello');    
    productRepository.save(product); // this is still ok

    product.getTags().add(tag); // this triggers Hibernate to cascade and save record into product_has_tag, but I don't want that! I expect 'tags' collection to be basically read-only from db perspective

    // create relation manually
    ProductHasTag pht = new ProductHasTag();
    pht.setProductId(product.getId());
    pht.setTagId(tag.getId());
    pht.setSpecial('special'); // the whole reason why I am doing this manually

    productHasTagRepository.save(pht); // here I am creating record in join table the second time, but I need this instance instead of the automatically generated

}

And now when this method finishes and Hibernate commits the session, I get duplicity error:

Duplicate entry for key 'PRIMARY'] [insert into product_has_tag (product_id,tag_id) values (?,?)]; 

Hibernate attempted to store record into the join table automatically and hence my custom code caused a duplicity.

Can this be fixed? I really need product object to hold newly added tags, I just don't want them to be saved to db.

0

There are 0 best solutions below