0

I have 2 tables, T_PLN and T_PLN_CST_PRD A generator (M2M.Q_PLN_ID) is defined in the database (Oracle) and should be used as the primary key for both tables.

While saving a new plan (which contains a CostPeriod), I want the generator to create a new primary key for T_PLN and use it in T_PLN_CST_PRD as a primary key as well.

Table T_PLN PLN_ID // primary key (long) generated by sequence // some more columns

Table T_PLN_CST_PRD PLN_ID // primary key - should be same one as in T_PLN table // some more columns

@Entity
@Table(schema = "M2M", name = "T_PLN")
public class Plan {

    @Id
    @SequenceGenerator(name = "m2mPlanId", sequenceName = "M2M.Q_PLN_ID")
    @GeneratedValue(generator = "m2mPlanId")
    @Column(name = "PLN_ID")
    Long id;

    @OneToOne(cascade = {CascadeType.ALL})
    @JoinColumn(name = "PLN_ID", table = "T_PLN_CST_PRD")
    CostPeriod costPeriod;

    ...   
}



@Entity
@Table(schema = "M2M", name = "T_PLN_CST_PRD")
public class CostPeriod {

    @Id
    @Column(name = "PLN_ID")
    Long planId;

    ...
}

Code above is missing something to connect both primary keys, but I'm not sure what...

I tried all sorts of relations (@OneToOne with mappedBy, @JoinColumn, adding Plan as a member of T_PLN_CST_PRD..) but couldn't get it to work due to verious reasons:

  • IdentifierGenerationException: ids for this class must be manually assigned before calling save():

  • IdentifierGenerationException: attempted to assign id from null one-to-one property

and others...

also tried the solution in: OneToOne between two tables with shared primary key with no luck.

How should this kind of relation be defined?

Community
  • 1
  • 1
Gilad Shahrabani
  • 706
  • 1
  • 6
  • 12

2 Answers2

1

Assuming JPA 2+ then you be able to easily map this as below:

@Entity
@Table(schema = "M2M", name = "T_PLN")
public class Plan {

    @Id
    @SequenceGenerator(name = "m2mPlanId", sequenceName = "M2M.Q_PLN_ID")
    @GeneratedValue(generator = "m2mPlanId")
    @Column(name = "PLN_ID")
    Long id;

    @OneToOne(cascade = {CascadeType.ALL}, mappedBy = "plan")
    CostPeriod costPeriod;
}



@Entity
@Table(schema = "M2M", name = "T_PLN_CST_PRD")
public class CostPeriod {

    @Id
    @OneToOne(cascade = {CascadeType.ALL})
    @JoinColumn(name = "PLN_ID")
    Plan plan;
}

Ensure you have set both sides of the relationship before persisting.

plan.setCostPeriod(costPeriod);
costPeriod.setPlan(plan);
Alan Hay
  • 22,665
  • 4
  • 56
  • 110
  • I though it was working, it's not failing on any error and the data is saved to the database. But the ID's are not the same. it seems that 2 different primary id's are created for the tables. – Gilad Shahrabani Feb 16 '16 at 11:15
  • it's not working now - complains about: "Requested bean is currently in creation: Is there an unresolvable circular reference?" – Gilad Shahrabani Feb 16 '16 at 11:43
  • I have checked my **original** solution against the book ProJPA2 and I do not see anything wrong. Post the code where you create and persist the entities. I fail to see, given these mappings, how CostPeriod can possibly be created with a different ID. I mean, where can this 'other' ID be coming from? – Alan Hay Feb 16 '16 at 12:10
  • After some investigation it seems that this was related to a DB trigger: `select M2M.Q_PLN_ID.NEXTVAL INTO :new.PLN_ID from dual;` which overriden the JPA query parameters and caused it to be inserted with a x+1 id. we removed it. – Gilad Shahrabani Feb 16 '16 at 13:05
0

I think your are missing @PrimaryKeyJoinColumn and MapsId for a bidirectional one-to-one shared primary key model.

Can you try the following mapping:

@Entity
@Table(schema = "M2M", name = "T_PLN")
public class Plan {

    @Id
    @SequenceGenerator(name = "m2mPlanId", sequenceName = "M2M.Q_PLN_ID")
    @GeneratedValue(generator = "m2mPlanId")
    @Column(name = "PLN_ID")
    Long id;

    @OneToOne(cascade = {CascadeType.ALL})
    @PrimaryKeyJoinColumn
    //OR
    //Instead of above @OneToOne and @PrimaryKeyJoinColumn use below:
    //@OneToOne(mappedBy="plan", cascade = {CascadeType.ALL})
    CostPeriod costPeriod;

    ...
}



@Entity
@Table(schema = "M2M", name = "T_PLN_CST_PRD")
public class CostPeriod {

    @Id
    @Column(name = "PLN_ID")
    Long planId;

    @OneToOne
    @JoinColumn(name = "id")
    @MapsId
    private Plan plan;  

    ...
}

And while saving:

        Plan p = new Plan();
        CostPeriod cp = new CostPeriod();
        p.costPeriod = cp;
        cp.plan = p;
        session.persist(p);
  • Wouldn't he want mappedBy on one side of the relationship rather than defining a join column on both sides? Additionally, adding table = "T_PLN_CST_PRD" will not force the join column to be in T_PLN_CST_PRD. I believe table would only be specified in case of a `@SecondaryTable` being defined for CostPeriod. – Alan Hay Feb 15 '16 at 16:54
  • @AlanHay My mistake. copy paste errors. Along with that I used IDENTITY as strategy. I like your simpler code. But then, I have updated the code. would like to hear any suggestions/comments. By the way, I didn't get your comment regarding SecondaryTable. Can you elaborate. – Madhusudana Reddy Sunnapu Feb 15 '16 at 17:28
  • This will works as well. it failed for me due to the same reason as in @Alan Hay answer. (DB trigger) – Gilad Shahrabani Feb 16 '16 at 13:10