sqlspringpostgresqlspring-bootspring-data-jpa

JPA query for deleting relations


@Entity
data class Election(
    @Id @UuidGenerator val electionId: String = "",
    var name: String = "",
    var description: String = "",
    var startDate: LocalDateTime? = null,
    var endDate: LocalDateTime? = null,
    var electionCode: String = "", // auto generated to be unique

    @ManyToOne(cascade = [CascadeType.ALL]) @Column(nullable = false) var electoralCommission: ElectoralCommission = ElectoralCommission(),

    @OneToMany(mappedBy = "elections") var candidates: MutableList<Candidate> = mutableListOf(),

    @OneToMany var eligibleVoters: MutableList<Voter> = mutableListOf(),

    @Enumerated(EnumType.STRING) var electionStatus: ElectionStatus = ElectionStatus.UPCOMING
)

@Entity
data class Voter(
    @Id
    @UuidGenerator
    @Column(name = "voter_id")
    private val _voterId: String = "",

    val name: String = "",
    val email: String = "",
    val phoneNumber: String = "",
    val dateOfBirth: LocalDateTime = LocalDateTime.now(),

    @Column(name = "voter_code", unique = true, length = 8, updatable = false) var voterCode: String = "",

    @Enumerated(EnumType.STRING) val voterStatus: VoterStatus = VoterStatus.REGISTERED,

    @ManyToOne
    @JoinColumn(name = "electoral_commission_id") val electoralCommission: ElectoralCommission? = null
) {
    fun toCandidate(election: Election): Candidate {
        return Candidate(
            name = name,
            email = email,
            phoneNumber = phoneNumber,
            voterCode = voterCode,
            candidateStatus = CandidateStatus.REGISTERED,
            election = election
        )
    }
}

The election entity has a list of voters (many to one, uni-directional). When someone votes, the relation of that voter with election will end. I am not able to figure out how to achieve that using jpa. I have tried doing query using election_voter table but jpa gives error that it can't parse the query. The Query was

delete from election_voter e where e.voter_voter_code = :voterCode


Solution

  • You mention in your question:

    I have tried doing query using election_voter table ...

    so lets start with the table definition for voter.

    CREATE TABLE voter
    (
        voter_id    UUID PRIMARY KEY,
        election_id UUID NULL,
        FOREIGN KEY (election_id) REFERENCES election (election_id)
    );
    

    This should allow voter to exists without an election.

    When running the code with just this, Hibernate will try to insert records into an intermediate table, so we need a modification in the Election class.

    @OneToMany 
    @JoinColumn(name="election_id") // let Hibernate know about the FK
    var eligibleVoters: MutableList<Voter> = mutableListOf()
    

    For detaching a voter from an election with the above, we'll need a native query:

    @NativeQuery("update voter set election_id = null where voter_id = :voterId")
    

    This is not desirable, so let's try to use a query like this:

    @Query("UPDATE Voter v SET v.electionId = NULL WHERE v.voterId = :voterId")
    

    Query says we need to add electionId to Voter:

    @Column(name = "election_id")
    val electionId: String = "" // or var if updating it here
    

    and change Election again

    @OneToMany(mappedBy = "electionId")
    var eligibleVoters: MutableList<Voter> = mutableListOf()