Skip to main content

Problem with @OneToOne mapping

7 replies [Last post]
suttridge_farm
Offline
Joined: 2006-01-27

I am using b34 and Derby. There is a parent table with a simple integer primary key, and a child table in a one-to-one relationship having the primary key of the parent table as its foreign key. The child entity has its primary key column set as read-only, but when deploying, I get the toplink error that there exists multiple writable mapping for the parent's primary key column.

In the parent entity :
@Id
@Column(name = "id", nullable = false)
public int getId() { return id; }
public void setId(int id) {
this.id = id;
}
@OneToOne
@JoinColumn(name = "id", referencedColumnName = "id")
public Child getChild() { return child; };
public void setChild(Child child) {
this.child = child;
}

In the child entity :-
@Id
@Column(name = "id", nullable = false, updatable = false)
public int getId() { return id; }
public void setId(int id) {
this.id = id;
}
@OneToOne(optional = false, mappedBy = "child")
public Parent getParent() { return parent; }
public void setParent(Parent parent) {
this.parent = parent;
}

The ejb3 persistence spec also shows that instead of the @JoinColumn on the parent entity's @OneToOne annotation, you can use the @PrimaryKeyJoinColumn. This results in the same errors on deploying.

As a further problem in this area, the java compiler (NetBeans 5.1) comes up with an error when using the @PrimaryKeyJoinColumns construct, as in :-
@OneToOne
@PrimaryKeyJoinColumns({
@PrimaryKeyJoinColumn(name = "id", referencedColumnName = "id")
})
public Child getChild() { return child; };
public void setChild(Child child) {
this.child = child;
}

- the error is "annotation type is not applicable to this kind of declaration", and it points to the @PrimaryKeyJoinColumns annotaion.

Anyone have any ideas on these issues, or are they bugs ?

Thanks,

Steve.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
mf125085
Offline
Joined: 2005-03-29

Hi Steve,

In a Parent-Child relationship, usually the Child cannot exist w/o the Parent, but the Parent can exist w/o the Child. So, the Child should be the owner of the relationship. In your mapping, the Parent is the owner. I'd propose the following mapping:

In the parent entity :
@Id
@Column(name = "id", nullable = false)
public int getId() { return id; }
public void setId(int id) {
this.id = id;
}

@OneToOne(mappedBy = "parent")
public Child getChild() { return child; };
public void setChild(Child child) {
this.child = child;
}

In the child entity :
@Id
public int getId() { return id; }
public void setId(int id) {
this.id = id;
}
@OneToOne(optional=false)
@JoinColumn(name = "id", referencedColumnName = "id", unique = true, nullable = false, updatable = false)
public Parent getParent() { return parent; }
public void setParent(Parent parent) {
this.parent = parent;
}

This would make the Child own the relationship, and the relationship is required for the Child. The spec should allow this relationsship mapping.

Your example looks like a perfect secondary table mapping, i.e. mapping the Parent to the primary and the child to the secondary table. Did you consider this option?

-- markus.

tware
Offline
Joined: 2005-06-24

FYI:

1. The next entity-persistence drop will likely include a way of setting mappings to be read-only. That will allow specification of the mappings with one small addition to the example provided in the forum post.
2. @PrimaryKeyJoinColumns for one-one mappings will be one of the next things we work on for our updates to comply with the PFD version of the spec.

ss141213
Offline
Joined: 2005-03-30

> but when deploying, I get the toplink error that
> there exists multiple writable mapping for the
> parent's primary key column.
>
The "multiple writable mappings exist" error is caused by the fact that both the PK column and FK column in Parent table are referring to the same column called "id". [b]So we have to make one of the two mappings read-only[/b]. Instead of marking one of them read-only, you probably made the PK column of Child table read-only. So try the following:

In the parent entity :
@Id
@Column(name = "id", nullable = false, [b]insertable=false, updatable=false[/b])
public int getId() { return id; }

Please also note that, instead of making the id read-only, you could also use insertable=false, updatable=false to mark the the one-to-one mapping read-only as follows:
([b]Don't[/b] try this alternative now because of https://glassfish.dev.java.net/issues/show_bug.cgi?id=163
)
In the parent entity :
@OneToOne
@JoinColumn(name = "id", referencedColumnName = "id", insertable=false, updatable=false)
public Child getChild() { return child; }

> The ejb3 persistence spec also shows that instead of
> the @JoinColumn on the parent entity's @OneToOne
> annotation, you can use the @PrimaryKeyJoinColumn.
The spec is [b]contradicting[/b] itself because the definition of PrimaryKeyJoinColumnn does not allow it right now. See more on this below.
> This results in the same errors on deploying.
How did you compile the above change that you could go onto deploy?
>
> As a further problem in this area, the java compiler
> (NetBeans 5.1) comes up with an error when using the
> @PrimaryKeyJoinColumns construct, as in :-
> @OneToOne
> @PrimaryKeyJoinColumns({
> @PrimaryKeyJoinColumn(name = "id",
> referencedColumnName = "id")
> })
> public Child getChild() { return child; };
> public void setChild(Child child) {
> this.child = child;
> }
>
> - the error is "annotation type is not applicable to
> this kind of declaration", and it points to the
> @PrimaryKeyJoinColumns annotaion.
You are seeing this javac error because of the way PrimaryKeyJoinColumns is defined in the spec. In section #9.1.31 of EJB 3 PFD spec, it is mentioned that:

[b]@Target({TYPE})[/b] @Retention(RUNTIME)
public @interface PrimaryKeyJoinColumns {
PrimaryKeyJoinColumn[] value();
}

This means, this annotation can only be used to annotate [b]TYPE[/b]. It definitely looks like a [b]bug in the spec[/b], because example #2 of the spec uses this annotation to annotate a field. I am almost certain that the annotation definition needs to change, only then you can use it to annotate a field or method. If you don't mind, file a bug in GlassFish (entity-persistence category) to fix the @PrimaryKeyJoinColumns source code.

Thanks,
Sahoo

>
> Anyone have any ideas on these issues, or are they
> bugs ?
>
> Thanks,
>
> Steve.

suttridge_farm
Offline
Joined: 2006-01-27

Hi Sahoo,

Thanks for the info. I have submitted a bug report for the @PrimaryKeyJoinColumns annotation source declaration - I agree, it should be @Target { TYPE, METHOD, FIELD } and not just @Target { TYPE }.

When I did the one test using @PrimaryKeyJoinColumn (one column only), there is no problem with the compiler, it was only when I had a composite primary key, and needed to use @PrimaryKeyJoinColumns that I couldn't compile.

I think I'll wait until things get more sorted out in the spec with regards to this one-to-one mapping - as I mentioned earlier, I can always get around it by using a one-to-many from parent to child, even though there will only ever be one child.

Thanks,

Steve.

mperezma
Offline
Joined: 2005-03-22

Hello Steve,

In this declaration:

@OneToOne
@JoinColumn(name = "id", referencedColumnName = "id")
public Child getChild() { return child; };

I think you shouldn't name the column like the primary key. May be something like will work:

@OneToOne
@JoinColumn(name = "child_id", referencedColumnName = "id")
public Child getChild() { return child; };

mariO

suttridge_farm
Offline
Joined: 2006-01-27

Hi mariO,

Yes, you are right (more or less). The ONLY way this thing works is to use the following constructs, where the child's primary key column name has a different name to the primary key column name that it is referencing :-

In the parent entity -
@Id
@Column(name = "id", nullable = false)
public int getId() { return id; }
public void setId(int id) {
this.id = id;
}
@OneToOne
@PrimaryKeyJoinColumn(name = "id", referencedColumnName = "child_id")
public Child getChild() { return child; };
public void setChild(Child child) {
this.child = child;
}

In the child entity -
@Id
@Column(name = "child_id", nullable = false)
public int getId() { return id; }
public void setId(int id) {
this.id = id;
}
@OneToOne(optional = false, mappedBy = "child")
public Parent getParent() { return parent; }
public void setParent(Parent parent) {
this.parent = parent;
}

None of the example constructs given in the EJB3 spec work at all for the one-to-one mapping (the ones where they use the @JoinColumn, nor the ones where they use the @PrimaryKeyJoinColumn where the column names are assumed to be identical).

This is all rather unsatisfactory, since I am constrained by the fact that the database with its tables and column names already exists, and I don't want to change them. The work-around I am using at the moment is to declare a @OneToMany from the parent to the child (corresponding @ManyToOne from the child back to the parent). This means that the parent has a collection of children, although this collection should always only ever have one item in it.

But I still feel that the specification is not being adhered to, and there is also the matter of the Java compiler error with the @PrimaryKeyJoinColumns annotation.

Regards,

Steve.

chris_delahunt
Offline
Joined: 2005-07-06

Hello Steve,

I'm not sure I'm following how you are using the PrimaryKeyJoinColumn annotation and think it maybe used incorrectly. From my understanding, this annotation is used in entity inheritance on the subclass. This allows the subclass to use a different table, and allows it to join to the superclass' table using any relationship.

From your initial post, it looks like the error you are getting deals with 1:1 mappings, which should not involve a PrimaryKeyJoinColumn annotation in this way.

The error you got, multiple writable mappings, is because you have specified the ID field and the FK in the 1:1 Parent->Child relation both to use the parent.id database field. This means that it can be set through both the parent.id attribute and the parent.child attribute, which is not allowed according to the spec since there is no way to determine which takes preceedence in a conflict. Once should be 'writable' the other should be set to read only.

Best Regards,
Chris