FreelancerTagCommandService.java

package de.mirkosertic.powerstaff.freelancer.command;

import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class FreelancerTagCommandService {

    private final FreelancerTagRepository tagRepository;

    public FreelancerTagCommandService(final FreelancerTagRepository tagRepository) {
        this.tagRepository = tagRepository;
    }

    /**
     * Ordnet einen Tag einem Freiberufler zu.
     * Wirft {@link DuplicateTagException} wenn der Tag bereits zugeordnet ist.
     * noRollbackFor: Aufrufer kann DuplicateTagException abfangen ohne die Transaktion zu poisonen.
     */
    @Transactional(noRollbackFor = DuplicateTagException.class)
    public FreelancerTag addTag(final long freelancerId, final long tagId) {
        if (tagRepository.existsByFreelancerIdAndTagId(freelancerId, tagId)) {
            throw new DuplicateTagException(freelancerId, tagId);
        }
        final var tag = new FreelancerTag();
        tag.setFreelancerId(freelancerId);
        tag.setTagId(tagId);
        try {
            return tagRepository.save(tag);
        } catch (final DataIntegrityViolationException e) {
            // Race condition: Duplicate zwischen existsBy-Check und save abfangen
            throw new DuplicateTagException(freelancerId, tagId);
        }
    }

    /**
     * Entfernt eine Tag-Zuordnung anhand der Zuordnungs-ID.
     */
    public void removeTag(final long freelancerTagId) {
        tagRepository.deleteById(freelancerTagId);
    }

    /**
     * Entfernt eine Tag-Zuordnung anhand von Freiberufler-ID und Tag-Entity-ID.
     */
    public void removeTagByTagId(final long freelancerId, final long tagId) {
        tagRepository.deleteByFreelancerIdAndTagId(freelancerId, tagId);
    }
}