1

I'm new to JPA. Suppose I have these two entities:

//Imports

@Entity
@Table(name="article", schema = "sch_client")
public class Article implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private int price;
    private int amount;

    //Getters & setters
}

And

@Entity
@Table(name="purchase", schema = "sch_client")
public class Purchase implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;

    @OneToMany
    private List<Article> listArticle;}

I want to have something like a purchase contains many articles.

My question is: is it possible with only @OneToMany in Purchase class that points to Article class to have the desired relationship (a purchase contains many articles). Or to use a @OneToMany annotation I have to add a @ManyToOne on Article class. If so, why is is mandatory to add the @ManyToOne? any explanation please. Thanks in advance.

ziMtyth
  • 1,008
  • 16
  • 32
  • Yes sure. Article doesn't need to keep a Purchase if it doesn't make sense. Have you tried it? it should work fine – Jack Flamp Sep 03 '17 at 13:20
  • @JackFlamp thats the problem I've tried it and no changes happened in the tables :/ nothing was added – ziMtyth Sep 03 '17 at 13:22
  • so you persisted and nothing happened? Were you able to save a Purchase but articles weren't saved, you mean? – Jack Flamp Sep 03 '17 at 13:23
  • No I did'nt try to persist, all I did is checking the tables in my database (PostGresQL) – ziMtyth Sep 03 '17 at 13:24
  • eh.. well you need to save stuff to put them in the database. are you using hibernate? – Jack Flamp Sep 03 '17 at 13:25
  • I'm using default JPA of NetBeans (EclipseLink), I will try to persist. I thought that once I add the annotation I will see changes in database (more specifically a foreign key added to Article that points Purchase) or am I wrong? correct me please if I'm wrong. – ziMtyth Sep 03 '17 at 13:27
  • It doesn't show as anything different in the tables. It's a uni-directional relationship in code, but in the database it'll still be a bi-directional relationship (due to how RDBMS works). – Kayaman Sep 03 '17 at 13:27
  • 1
    @ZiMtyth here is an example of how to implement a unidirectional relationship. They use a jointable. http://www.java2s.com/Code/Java/JPA/OneToManyUnidirectionalMapping.htm – Jack Flamp Sep 03 '17 at 13:39
  • 1
    @ZiMtyth You might want to look at `Cascade` too so that when a Purchase is deleted, e.g. the Articles are deleted with it, – Jack Flamp Sep 03 '17 at 13:42
  • I tried to do as done in that example, but nothing was added to the tables, I noticed something weird, when I try to delete Purchase, an error popup : I can't delete because of foreign key, same error when trying to delete Article, it's like the foreign key was added but I can't see it in PostgreSQL, I don't know why, I tried to persist Article it worked, I didn't try to persist Purchase I haven't build it's view and controller yet but will do and try the persist. Thanks a lot for your help @JackFlamp I really appreciated it, I will tell once I finish my tests. – ziMtyth Sep 03 '17 at 14:03
  • @Kayaman in fact, it does show difference in database, it adds a join table, feel free to check my answer to be aware of the solution =). – ziMtyth Sep 03 '17 at 14:42
  • @Kayaman I executed the code shown in the answer I got a join table that contains both primary keys of Purchase and Article. Can you please explain to me why I get this join table as a result (I've just used one annotation which is "@OneToMany" in Purchase). – ziMtyth Sep 03 '17 at 14:50
  • Don't use @JoinTable, put only the @OneToMany annotation in `Purchase` (leave out @ManyToOne from `Article`), and the join table shouldn't be generated. Instead you should only get `fk_purchase` (or similarly named) column in your `article` table. That's the classic relational one-to-many relationship. – Kayaman Sep 03 '17 at 14:56
  • @Kayaman I tried what you suggested, it created a join table in public shema, I understand that in "@OneToMany" a join table is created always. – ziMtyth Sep 03 '17 at 15:04
  • Then you understood wrong, because that's not true. A join table is not necessary for 1-* relationships. You could try adding an explicit @JoinColumn to see if that would prevent the over eager creation of link tables. – Kayaman Sep 03 '17 at 15:08
  • @Kayaman I agree with you in "A join table is not necessary for 1-* relationships", I don't understand why it creates a join table in my case. – ziMtyth Sep 03 '17 at 15:10
  • Try with a @JoinColumn like I said. – Kayaman Sep 03 '17 at 15:13
  • @Kayaman you were right I figured out how to do that, you can check my update if you want to, thanks for effort =) – ziMtyth Sep 04 '17 at 09:48
  • Remove the: *serialVersionUID* -- it's an antipattern – Software Engineer Sep 04 '17 at 09:50
  • @EngineerDollery can you please give me more explanations (maybe a link) I don't understand what do you mean. – ziMtyth Sep 04 '17 at 09:52
  • 1
    It's used for object de/serialization and is intended to allow you to de/serialize an old version of a class using newer bytecode. The idea is nonsense -- you have to manually change the value, and keep it unique across your jvm, every time you change your class in a way that's incompatible with older versions. If you completely leave it out the compiler does this for you. If you want to control it, you can use serialver tool to fix it later. Your classes aren't even de/serialized in the classical sense so it's doubly pointless. Unless you totally understand it, you shouldn't do it. – Software Engineer Sep 04 '17 at 10:00
  • 1
    https://stackoverflow.com/questions/285793/what-is-a-serialversionuid-and-why-should-i-use-it – Software Engineer Sep 04 '17 at 10:03
  • @EngineerDollery in the link you gave me it's written "Therefore, to guarantee a consistent serialVersionUID value across different java compiler implementations, a serializable class must declare an explicit serialVersionUID value.", so you might be wrong :) or I misunderstood it. – ziMtyth Sep 04 '17 at 10:10
  • 1
    Read this answer: https://stackoverflow.com/a/285827/2051454 – Software Engineer Sep 04 '17 at 10:11
  • 1
    "If you ignore them for now, and find later that you need to change the class in some way but maintain compatibility w/ old version of the class, you can use the JDK tool serialver to generate the serialVersionUID on the old class, and explicitly set that on the new class. (Depending on your changes you may need to also implement custom serialization by adding writeObject and readObject methods - see Serializable javadoc or aforementioned chapter 11.)" – Software Engineer Sep 04 '17 at 10:11
  • 1
    For the problem you present here, your entities should not be serializable -- it is not required by jpa. Even if it were, you are unlikely to change objects at runtime in a way that requires de/serialization. If you don't understand exactly what serialVersionUID is and does and how you're using it then you shouldn't have it in your code. – Software Engineer Sep 04 '17 at 10:18
  • @EngineerDollery well I think that an Entity must be serializable, at least in my context, where I work on a web application, in my case my object will be sent on a network, in an XML or JSON form so it needs to be serialised. Here http://docs.oracle.com/javaee/6/tutorial/doc/bnbqa.html you can read this: "If an entity instance is passed by value as a detached object, such as through a session bean’s remote business interface, the class must implement the Serializable interface." – ziMtyth Sep 04 '17 at 10:35

1 Answers1

2

First of all, I have write a misleading title, I will change it to make it more accurate:

Old title : In JPA, is it possible to use @OneToMany without using @ManyToOne?

New title : @OneToMany does not create the join table.

As I said, I'm new to JPA, my problem can appear dumb, I could delete the question, but I decided to keep it in case someone someday will face similar situation, it can help!

The join table of Purchase and Article was created every time I executed the code very normally, but I didn't notice!, I was checking the logs of NetBeans and didn't see the join table, I was misled by those logs, I think that a join table doesn't appear in the logs (I hope that someone can confirm this information and make an edit of this answer).

I have created Purchase and Article in a new schema named: sch_sales. and the join table was created in public schema (PostgreSQL).

So, to make it more correct I added schema to @JoinTable as shown below, like this I will have all my tables in the same schema.

@Entity
@Table(name="purchase", schema = "sch_sales")
public class Purchase implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;

    @OneToMany
    @JoinTable(name="join_purchase_article", schema = "sch_sales", joinColumns = @JoinColumn(name="sales_fk"), inverseJoinColumns = @JoinColumn(name="article_fk"))
    private List<Article> listArticle;

 }

UPDATE :

I was having a 3rd table created containing the id of Purchase and Article (a join table) which is obviously not correct.

The normal "behavior" is to have an id_purchase column added in Article, in this page I have find how to have such a result.

To have the desired result, I used the code below:

@Entity
@Table(name="purchase", schema = "sch_sales")
public class Purchase implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;

    @OneToMany
    @JoinColumn(name="id_purchase")
    private List<Article> listArticle;

 }
Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
ziMtyth
  • 1,008
  • 16
  • 32