Home > Software design >  spring-data: foreign-key as a primary
spring-data: foreign-key as a primary

Time:11-06

I have a table with 100 fields, which i want to split into 3-4 tables. While creating, the primary key for one of the table would become foreign and primary key for remaining tables.

I tried something like this

@Entity
public class Entity1 implements Serializable {
    @Id
    @Column(name = "id")
    private String id;
    
    ...
}

@Entity
@IdClass(Entity1.class)
public class Entity2 implements Serializable {
    @Id
    @OneToOne
    @JoinColumn(name = "id",  referencedColumnName = "id")
    private Entity1 id;
    
    ...
}


@Repository
public interface Entity1Repository extends JpaRepository<Entity1, String> {
    Optional<Entity1> findById(String id);
}

@Repository
public interface Entity2Repository extends JpaRepository<Entity2, Entity1> {
    Optional<Entity2> findById(Entity1 id);
}

It is creating the tables, but giving error as This class [class xyz.Entity2] does not define an IdClass

Also looked at few of the references, like use of @MapsId. But non of the solution was working for me. Any inputs?

Thanks

Venkata Madhu

CodePudding user response:

I don't know what approach you were trying with the MapsId feature. But it certainly can help you in this situation. Here is your example code with MapsId implementation:

@Entity
public class Entity1 implements Serializable {
    @Id
    private Long id;
    private String name;

    @OneToOne(mappedBy = "entity1")
    private Entity2 entity2;

    @OneToOne(mappedBy = "entity1")
    private Entity3 entity3;
}

This is the parent class that will only hold the reference of the OneToOne association. The child will be the owner of the relation.

Here is your Entity2 child:

@Entity
public class Entity2 implements Serializable {
    @Id
    private Long id;
    private String value;

    @MapsId
    @OneToOne(cascade = CascadeType.PERSIST)
    @JoinColumn(name = "id")
    private Entity1 entity1;
}

As you said you will be splicing your main entity into multiple child I went ahead and used another child entity to clarify my point:

@Entity
public class Entity3 implements Serializable {
    @Id
    private Long id;
    private String value;

   @MapsId
   @OneToOne(cascade = CascadeType.PERSIST)
   @JoinColumn(name = "id")
   private Entity1 entity1;
}

You may have noticed by now that I am using:

  1. @JoinColumn annotation in the child classes to tell hibernate that use the id column of the child to map this OneToOne relation with Entity1 class.
  2. I am using cascadeType = PERSIST in the child classes.

The first step is required to perform the relation mapping with id. And the second step is required to save all 3 entities into the database in the same transaction. More on this later.

Let's see how you can now save all of these entities into the database.

@Transactional
public void saveEntities() {
    Long id = 1L;
    Entity1 entity1 = new Entity1();
    entity1.setId(id);
    entity1.setName("Test1Entity1: "   id);

    Entity2 entity2 = new Entity2();
    entity2.setValue("entity2Value: "   id);
    entity2.setEntity1(entity1);
    entity2Repository.save(entity2);
    Entity3 entity3 = new Entity3();
    entity3.setValue("Test3Entity:"   id);
    entity3.setEntity1(entity1);
    entity3Repository.save(entity3);
}

Notice that I didn't save the Entity1 class with the repository. It is being saved by the cascadeType=PERSIST param that I passed into the OneToOne annotation operation.

This should be able to save all the entities along with the parent one.

Note: Earlier I was saying about cascadeType=PERSIST and why it was important here. If you don't use that then you had to call the Entity1 repository method explicitly to save its object to the database first then you could set them into the child entities. But if you do so, hibernate will throw: a different object with the same identifier value was already associated with the session exception while you perform save operation on child entity.

CodePudding user response:

Try using @PrimaryKeyJoinColumn:

@Entity
public class Entity1 {
    @Id
    @Column(name = "id")
    private String id;
    
    ...

    @OneToOne(mappedBy = "entity1")
    @PrimaryKeyJoinColumn
    private Entity2 entity2;
}

@Entity
public class Entity2 {
    @Id
    @Column(name = "entity1_id")
    private String id;
    
    ...

    @MapsId
    @OneToOne(cascade = CascadeType.PERSIST)
    @JoinColumn(name = "entity1_id)
    private Entity1 entity1;
}

public interface Entity2Repository extends JpaRepository<Entity2, String>

Test:

@Test
void save() {
    Entity1 entity1 = new Entity1();
    entity1.setId("iddddd");

    Entity2 entity2 = new Entity2();
    detail.setEntity1(entity1);

    this.entity2Repository.save(entity2);
}

Reference: Shared Primary Key

  • Related