Skip to main content

Autogeneration of unidirectional OneToMany mapping fails

4 replies [Last post]
cayhorstmann
Offline
Joined: 2003-06-13

I am trying to set up a straightforward unidirectional OneToMany relationships, as in section 2.1.8.5.1 of the persistence spec.

@Entity
public class Choice { ... }

@Entity
public class Question {

private Collection choices;
@OneToMany
public Collection getChoices() {
return choices;
}
public void setChoices(Collection choices) {
this.choices = choices;
}
. . .
}

GlassFish makes the QUESTION_CHOICE table, as specified in the spec. But the table has no columns beyond a useless ID! I expected columns QUESTION_ID and CHOICE_ID.

Naturally, when trying to persist something, I get a stupid error:

Columns of type 'VARCHAR' cannot hold values of type 'INTEGER'. Error Code: -1
Call:INSERT INTO QUESTION_CHOICE (ID) VALUES (307)

This is with build 31.

Does anyone know a workaround?

Cay

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
cayhorstmann
Offline
Joined: 2003-06-13

Sure, here is a shar file. (You may need to clean up the smiley face before executing. Thanks so much, CollabNet...)

I implemented exactly the situation of section 2.8.1.5.1, and it builds a useless table EMPLOYEE_ANNUALREVIEW with no columns except ID.
This is with build31.

#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.2.1).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 2005-12-21 17:44 PST by .
# Source directory was `/home/cay/books/ee4elvis/code/spec28151/temp'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
# This format requires very little intelligence at unshar time.
# "if test", "echo", "mkdir", and "sed" may be needed.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 2922 -rw-r--r-- build.xml
# 480 -rw-r--r-- conf/persistence.xml
# 540 -rw-r--r-- conf/application.xml
# 487 -rw-r--r-- src/elvis/entity/AnnualReview.java
# 766 -rw-r--r-- src/elvis/entity/Employee.java
# 306 -rw-r--r-- src/elvis/client/Client.java
# 98 -rw-r--r-- src/elvis/session/Test.java
# 748 -rw-r--r-- src/elvis/session/TestBean.java
#
echo=echo
if mkdir _sh08415; then
$echo 'x -' 'creating lock directory'
else
$echo 'failed to create lock directory'
exit 1
fi
# ============= build.xml ==============
if test -f 'build.xml' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'build.xml' '(file already exists)'
else
sed 's/^X//' << 'SHAR_EOF' > 'build.xml' &&
X
X
X
X
X
X
X
X
X
X
X

X
X
X
X
X
X

X
X
X
X
X
X
X
X
X

X

X

X
X

X
X
X
X
X
X

X
X
X

X

X

X
X

X
X X appxml="${conf.dir}/application.xml" basedir="${build.dir}/jar"/>
X

X
X
X X classname="org.apache.tools.ant.taskdefs.optional.sun.appserv.DeployTask">
X
X
X location="${appserver.dir}/lib/sun-appserv-ant.jar"/>
X

X

X X verify="true" user="admin" password="adminadmin"
X dropandcreatetables="true" asinstalldir="${appserver.dir}"/>
X

X
X
X X classname="org.apache.tools.ant.taskdefs.optional.sun.appserv.UndeployTask">
X
X
X location="${appserver.dir}/lib/sun-appserv-ant.jar"/>
X

X

X X user="admin" password="adminadmin"
X droptables="true" asinstalldir="${appserver.dir}"/>
X

X
X
X
X
X
X
X
X

X

X

X
X
X
X

X
X

SHAR_EOF
: || $echo 'restore of' 'build.xml' 'failed'
fi
# ============= conf/persistence.xml ==============
if test ! -d 'conf'; then
mkdir 'conf'
fi
if test -f 'conf/persistence.xml' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'conf/persistence.xml' '(file already exists)'
else
sed 's/^X//' << 'SHAR_EOF' > 'conf/persistence.xml' &&
X

X

X
X jdbc/__default
X
X
X
X value="oracle.toplink.essentials.platform.database.DerbyPlatform"/>
X

X

X SHAR_EOF
: || $echo 'restore of' 'conf/persistence.xml' 'failed'
fi
# ============= conf/application.xml ==============
if test -f 'conf/application.xml' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'conf/application.xml' '(file already exists)'
else
sed 's/^X//' << 'SHAR_EOF' > 'conf/application.xml' &&
X
X X xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
X xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd">
X spec28151
X
X ejb.jar
X

X

X
SHAR_EOF
: || $echo 'restore of' 'conf/application.xml' 'failed'
fi
# ============= src/elvis/entity/AnnualReview.java ==============
if test ! -d 'src'; then
mkdir 'src'
fi
if test ! -d 'src/elvis'; then
mkdir 'src/elvis'
fi
if test ! -d 'src/elvis/entity'; then
mkdir 'src/elvis/entity'
fi
if test -f 'src/elvis/entity/AnnualReview.java' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'src/elvis/entity/AnnualReview.java' '(file already exists)'
else
sed 's/^X//' << 'SHAR_EOF' > 'src/elvis/entity/AnnualReview.java' &&
Xpackage elvis.entity;
X
Ximport java.io.Serializable;
X
Ximport javax.persistence.Entity;
Ximport javax.persistence.GeneratorType;
Ximport javax.persistence.Id;
X
X@Entity
Xpublic class AnnualReview implements Serializable {
X private int id;
X private String text;
X
X @Id(generate=GeneratorType.AUTO)
X public int getId() {
X return id;
X }
X
X public void setId(int id) {
X this.id = id;
X }
X
X public String getText() {
X return text;
X }
X
X public void setText(String text) {
X this.text = text;
X }
X}
SHAR_EOF
: || $echo 'restore of' 'src/elvis/entity/AnnualReview.java' 'failed'
fi
# ============= src/elvis/entity/Employee.java ==============
if test -f 'src/elvis/entity/Employee.java' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'src/elvis/entity/Employee.java' '(file already exists)'
else
sed 's/^X//' << 'SHAR_EOF' > 'src/elvis/entity/Employee.java' &&
Xpackage elvis.entity;
X
Ximport java.io.Serializable;
Ximport java.util.Collection;
X
Ximport javax.persistence.Entity;
Ximport javax.persistence.GeneratorType;
Ximport javax.persistence.Id;
Ximport javax.persistence.OneToMany;
X
X@Entity
Xpublic class Employee implements Serializable {
X private String id;
X private String name;
X private Collection reviews;
X
X @Id(generate=GeneratorType.AUTO)
X public String getId() {
X return id;
X }
X public void setId(String id) {
X this.id = id;
X }
X public String getName() {
X return name;
X }
X public void setName(String text) {
X this.name = text;
X }
X @OneToMany
X public Collection getReviews() {
X return reviews;
X }
X public void setReviews(Collection reviews) {
X this.reviews = reviews;
X }
X}
SHAR_EOF
: || $echo 'restore of' 'src/elvis/entity/Employee.java' 'failed'
fi
# ============= src/elvis/client/Client.java ==============
if test ! -d 'src/elvis/client'; then
mkdir 'src/elvis/client'
fi
if test -f 'src/elvis/client/Client.java' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'src/elvis/client/Client.java' '(file already exists)'
else
sed 's/^X//' << 'SHAR_EOF' > 'src/elvis/client/Client.java' &&
Xpackage elvis.client;
X
Ximport elvis.session.Test;
Ximport javax.naming.InitialContext;
X
Xpublic class Client
X{
X public static void main(String[] args) throws Exception
X {
X InitialContext ctx = new InitialContext();
X
X Test test = (Test) ctx.lookup(Test.class.getName());
X test.test();
X }
X}
SHAR_EOF
: || $echo 'restore of' 'src/elvis/client/Client.java' 'failed'
fi
# ============= src/elvis/session/Test.java ==============
if test ! -d 'src/elvis/session'; then
mkdir 'src/elvis/session'
fi
if test -f 'src/elvis/session/Test.java' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'src/elvis/session/Test.java' '(file already exists)'
else
sed 's/^X//' << 'SHAR_EOF' > 'src/elvis/session/Test.java' &&
Xpackage elvis.session;
X
Ximport javax.ejb.Remote;
X
X@Remote
Xpublic interface Test {
X void test();
X}
SHAR_EOF
: || $echo 'restore of' 'src/elvis/session/Test.java' 'failed'
fi
# ============= src/elvis/session/TestBean.java ==============
if test -f 'src/elvis/session/TestBean.java' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'src/elvis/session/TestBean.java' '(file already exists)'
else
sed 's/^X//' << 'SHAR_EOF' > 'src/elvis/session/TestBean.java' &&
Xpackage elvis.session;
X
Ximport java.util.ArrayList;
X
Ximport javax.ejb.Stateless;
Ximport javax.persistence.EntityManager;
Ximport javax.persistence.PersistenceContext;
X
Ximport elvis.entity.AnnualReview;
Ximport elvis.entity.Employee;
X
X@Stateless
Xpublic class TestBean implements Test {
X @PersistenceContext(unitName="defaultPersistenceUnit")
X private EntityManager em;
X
X public void test() {
X AnnualReview r = new AnnualReview();
X r.setText("Excellent performer. Deserves promotion.");
X em.persist(r);
X Employee e = new Employee();
X e.setName("Harry Hacker");
X ArrayList reviews = new ArrayList();
X reviews.add(r);
X e.setReviews(reviews);
X em.persist(e);
X }
X}
SHAR_EOF
: || $echo 'restore of' 'src/elvis/session/TestBean.java' 'failed'
fi
rm -fr _sh08415
exit 0

pramodgo
Offline
Joined: 2005-04-05

Thanks for U example. This is a bug in our code.
The issue here is for both the entities elvis.entity.Employee and elvis.entity.AnnualReview the primary keys are defined to be the same : "id". In this scenario the joint table that gets created has just one "id" column and we are not sure which "id" field is it actually referring to.

Additionally the latest persistence spec has clarified this scenario further. We should be explicitiy creating the table EMPLOYEE_REVIEW with the fields
as EMPLOYEE_ID and ANNUALREVIEW_ID.

I have copied the clarification that has been made in the latest spec for the section 2.1.8.5.1 :

"There is a join table that is named A_B (owner name first). This join table has two foreign key
columns. One foreign key column refers to table A and has the same type as the primary key of
table A. The name of this foreign key column is formed as the concatenation of the following:
the name of entity A; "_"; the name of the primary key column in table A. The other foreign
key column refers to table B and has the same type as the primary key of table B and there is a
unique key constraint on it. The name of this foreign key column is formed as the concatenation
of the following: the name of the relationship property or field of entity A; "_"; the name
of the primary key column in table B."

Until we fix this issue at our end, you could define the fields to be employeeId and annualReviewId in Employee.java and AnnualReview.java respectively.

pramodgo
Offline
Joined: 2005-04-05

Hi Cay
Could U post Ur classes so that we could duplicate this issue.

Thanks
Pramod

pkrogh
Offline
Joined: 2005-10-19

As a workaround you should be able to create the table manually and specfify the @JoinTable annotation.