Search code examples
springjpa

Entity Does not define an IdClass


I have 2 entites, one is User and the second one is ExternalAccount.

Here the mysql create code:

CREATE TABLE `User` (
    `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `firstName` VARCHAR(100) NOT NULL COLLATE 'utf8_unicode_ci',
    `lastName` VARCHAR(100) NOT NULL COLLATE 'utf8_unicode_ci',
    `date_add` DATETIME NOT NULL DEFAULT current_timestamp(),
    PRIMARY KEY (`id`) USING BTREE
)
ENGINE=InnoDB
AUTO_INCREMENT=15
;


CREATE TABLE `ExternalAccount` (
    `user_id` INT(10) UNSIGNED NOT NULL,
    `id_external_account` VARCHAR(100) NOT NULL COLLATE 'utf8_unicode_ci',
    `date_add` DATETIME NOT NULL DEFAULT current_timestamp(),
    PRIMARY KEY (`user_id`) USING BTREE,
    INDEX `id_external_account` (`id_external_account`) USING BTREE,
    CONSTRAINT `FK_user` FOREIGN KEY (`user_id`) REFERENCES `User` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB
;

Here my 3 generate entities:

package com.namirial.raws.timestamp.persistence.entities;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.annotation.CreatedDate;

import java.time.LocalDateTime;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Entity
@IdClass(ExternalAccountId.class)
@Table(name = "ExternalAccount", schema = "my_db")
public class ExternalAccount {

    @Id
    @OneToOne
    @JoinColumn(name = "user_id", referencedColumnName = "id")
    private User user;

    @CreatedDate
    @Column(name = "date_add", nullable = false)
    private LocalDateTime dateAdd = LocalDateTime.now();

    @Column(name = "id_external_account", nullable = false, length = 100, unique = true)
    private String idExternalAccount;
public class ExternalAccountId implements Serializable {

    User user;
}
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "User", schema = "my_db")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Integer id;

    @Column(name = "first_name", length = 100, nullable = false)
    private String firstName;

    @Column(name = "last_name", length = 100, nullable = false)
    private String lastName;

    @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
    private ExternalAccount externalAccount;


}

But When i try to save an externalAccount after creating an User (use UserRepo) i get an Entity Does not define an IdClass. I am using Java 17 with webflux and JpaRepository


public class UserBusinessServices {

public void createUser(){
 User user = new User();
user.setFirstName("JOHN");
user.setLastName("MOT");
user.setExternalAccount(new ExternalAccount(...));
userRepo.save(user);
}

}

Why I get this error? The strange thing is that I cannot found any example with such database structure, the only solution that I found is to use composite primary key on ExternalAccount with unique index on userid... Is the database structure incompatible with the framework? Thank you in advice.

save values on db and understand if the database structure is wrong


Solution

    1. You don't need @IdClass since you don't have a composite primary key. Hence you would need only 2 entities
    2. Use @MapsId since the ExternalAccount table shares its primary key (user_id) with User table (id).
    3. UserId should be present in ExternalAccount entity having @Id
    4. ExternalAccount is the owner's side of the OnetoOne relationship. So User should have mappedBy.

    This should be the entities:

    package com.namirial.raws.timestamp.persistence.entities;
    
    import jakarta.persistence.*;
    import lombok.AllArgsConstructor;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import lombok.Setter;
    import org.springframework.data.annotation.CreatedDate;
    
    import java.time.LocalDateTime;
    
    @NoArgsConstructor
    @AllArgsConstructor
    @Getter
    @Setter
    @Entity
    @Table(name = "ExternalAccount", schema = "my_db")
    public class ExternalAccount {
    
        @Id
        private Integer userId;
    
        @OneToOne
        @MapsId("userId")
        @JoinColumn(name = "user_id", referencedColumnName = "id")
        private User user;
    
        @CreatedDate
        @Column(name = "date_add", nullable = false)
        private LocalDateTime dateAdd = LocalDateTime.now();
    
        @Column(name = "id_external_account", nullable = false, length = 100, unique = true)
        private String idExternalAccount;
    
    @NoArgsConstructor
    @AllArgsConstructor
    @Getter
    @Setter
    @Entity
    @Table(name = "User", schema = "my_db")
    public class User {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "id", nullable = false)
        private Integer id;
    
        @Column(name = "first_name", length = 100, nullable = false)
        private String firstName;
    
        @Column(name = "last_name", length = 100, nullable = false)
        private String lastName;
    
        @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
        private ExternalAccount externalAccount;
    
    
    }
    

    You can read about shared primary keys here: https://thorben-janssen.com/hibernate-tips-same-primary-key-one-to-one-association/

    You can read about owners side of relationship here: What is the "owning side" in an ORM mapping?