3

I am trying to create a @OneToMany relationship in JPA using the following table structure.

+----------------+
| CATALOG        |
+----------------+
| catalogId : PK |
| name           |
+----------------+
      |
+----------------+
| PRODUCT        |
+----------------+
| productId : PK |
| name           |
| catalogId : FK |
+----------------+

I have defined the classes as

@Entity
public class Catalog {
    @Id
    @GeneratedValue
    long catalogId;

    @OneToMany(cascade = CascadeType.ALL, 
               orphanRemoval = true, fetch = FetchType.LAZY)
    @JoinColumn(name = "catalogId", 
               nullable = false, insertable = true, updatable = true)
    Set<Product> products = new HashSet<>();

    String name;
}

@Entity
public class Product {
    @Id
    @GeneratedValue
    long productId;

    String name;
}

However, when I try to persist the catalog object, EclipseLink does not put in the catalogId value as expected and I get a SQL constraint violation that there is a null.

Also, I don't need nor want a @ManyToOne on the Product end.

The persistence code:

final Catalog catalog = new Catalog();
catalog.name = text;
final Product p = new Product();
p.name = text;
catalog.products.add(p);
em.persist(catalog);

The stack trace:

javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: Column 'CATALOGID'  cannot accept a NULL value.
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1367)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1295)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1301)
    at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:866)
    at net.trajano.maven_jee6.ws_mdb_ejb_web.TextMessages.putText(TextMessages.java:61)
Archimedes Trajano
  • 35,625
  • 19
  • 175
  • 265
  • What database are you using? Is the id column auto increment or do you have some sequence generator? Try to give the id a value and save if it works. – Bhesh Gurung Jun 15 '12 at 05:38
  • I think your problem isn't related to relationship, I think it is related to primary key declaration. See my answer. – artaxerxe Jun 15 '12 at 05:38
  • I'm using an in-memory derby instance with the Hibernate Entity Manager (although EclipseLink or OpenJPA at final deployment) – Archimedes Trajano Jun 15 '12 at 05:39

3 Answers3

7

When you give ownership of a column on one table, Product, to a different entity, Catalog, that column is going to be updated and maintained as part of persisting Catalog, not as part of persisting Product. The framework is going to create a product, but then because the FK column is "owned" by a different entity, it's going to populate that with a separate update statement that is generated when the Catalog is persisted. You need to either make the column nullable or put ownership of the column on the same entity that maps the table.

Affe
  • 47,174
  • 11
  • 83
  • 83
  • I do know it works if I make it nullable=true, but that does not really conform to the fact that there can't be any product without the catalog – Archimedes Trajano Jun 15 '12 at 05:33
  • I didn't write the spec, just explaining why nullable=true is required for it to work :) Giving an entity ownership of a column that's defined on a different table is in general a PITA – Affe Jun 15 '12 at 05:48
  • Sucky as it sounds, this is the correct answer :/ I would think a DBA would get mad if he gets rid of this constraint because it is not correct data modelling wise. – Archimedes Trajano Jun 15 '12 at 05:58
  • The JPA spec is really crafted from the point of view of modelling an object domain and letting the schema be auto-magic. The whole idea of an FK embedded in a data table rather than using a join table is basically treated as 'legacy support' right out of the gate. – Affe Jun 15 '12 at 06:01
1

I think it is due to the lack of @GeneratedValue annotation (with Postgres). From my experience, with Postgres, your annotating does not work. You need to specify the @SequenceGenerator and @GeneratedValue. Here's an example:



    @SequenceGenerator(name = "lan_generator", sequenceName = "lan_pkey_seq")
    @Id
    @GeneratedValue(generator = "lan_generator")
    @Column(name="id", unique=true, nullable=false)
    private Integer id;

If you are working with other databases, try to deal with yours. Anyway, at the giveb link, you have a possible solution when you are working with MySQL. Probably it will work also with your "in-memory derby instance".

How to annotate MYSQL autoincrement field with JPA annotations

Community
  • 1
  • 1
artaxerxe
  • 6,281
  • 21
  • 68
  • 106
0

Make product class like this and try again -

@Entity
public class Product {
    @Id
    @GeneratedValue
    long productId;

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name = "catalogId", updatable = false, insertable = false, nullable=false)  
    private Catalog  catalog ;

    String name;
}
Pramod Kumar
  • 7,914
  • 5
  • 28
  • 37