-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 
Author Message
 Post subject: foreign key referencing another foreign key column
PostPosted: Sun Mar 30, 2014 5:16 pm 
Newbie

Joined: Sun Mar 30, 2014 5:06 pm
Posts: 2
Among others, I have the following tables (not showing non-relevant columns):
Code:
CREATE TABLE sources (
  id SERIAL NOT NULL,
  CONSTRAINT sources_pk
    PRIMARY KEY (id));

CREATE TABLE notes (
  id     SERIAL NOT NULL,
  source int4 NOT NULL,
  CONSTRAINT notes_pk
    PRIMARY KEY (id),
  CONSTRAINT notes_uq
    UNIQUE (id, source));
ALTER TABLE notes ADD CONSTRAINT notes_source_fk
  FOREIGN KEY (source) REFERENCES sources (id) ON UPDATE RESTRICT ON DELETE RESTRICT;

CREATE TABLE pages (
  id     SERIAL NOT NULL,
  source int4 NOT NULL,
  CONSTRAINT pages_pk
    PRIMARY KEY (id),
  CONSTRAINT pages_id_source_uq
    UNIQUE (id, source));
ALTER TABLE pages ADD CONSTRAINT pages_source_fk
  FOREIGN KEY (source) REFERENCES sources (id) ON UPDATE RESTRICT ON DELETE RESTRICT;

CREATE TABLE note_fragments (
  id     SERIAL NOT NULL,
  source int4 NOT NULL,
  page   int4 NOT NULL,
  CONSTRAINT note_fragments_pk
    PRIMARY KEY (id),
  CONSTRAINT note_fragments_uq
    UNIQUE (id, source));
ALTER TABLE note_fragments ADD CONSTRAINT note_fragments_source_page_fk
  FOREIGN KEY (page, source) REFERENCES pages (id, source) ON UPDATE RESTRICT ON DELETE RESTRICT;

CREATE TABLE note_fragment_assignments (
  id               SERIAL NOT NULL,
  note            int4 NOT NULL,
  note_source     int4 NOT NULL,
  fragment        int4 NOT NULL,
  fragment_source int4 NOT NULL,
  CONSTRAINT note_fragment_assignments_pk
    PRIMARY KEY (id),
  CONSTRAINT note_fragment_assignments_ck
    CHECK (note_source = fragment_source));
ALTER TABLE note_fragment_assignments ADD CONSTRAINT note_fragment_assignments_note_note_source_fk FOREIGN KEY (note, note_source) REFERENCES notes (id, source) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE note_fragment_assignments ADD CONSTRAINT note_fragment_assignments_fragment_fragment_source_fk FOREIGN KEY (fragment, fragment_source) REFERENCES note_fragments (id, source) ON UPDATE RESTRICT ON DELETE RESTRICT;

The business logic behind these tables is as following:
  • A source is a set of pages.
  • A source contains notes.
  • Notes are made of fragments (which are placed on pages). Note-to-fragment relation is n:m. It is prohibited to form one note out of fragments from different sources.
I know that maybe the design is not beautiful and the source column is redundant, but I have found no other solution to maintain the integrity without writing explicit triggers.

When trying to implement the Java code, I have prepared, i.a., the following entity classes:
Source.java
Code:
@Entity
@Table(name = "sources")
public class Source implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;

    @OneToMany(mappedBy = "source")
    private Collection<Note> noteBySourceCollection;

    @OneToMany(mappedBy = "source")
    private Collection<Page> pageBySourceCollection;

    // getters, setters etc.
}

Note.java
Code:
@Entity
@Table(name = "notes")
public class Note implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;

    @JoinColumn(name = "source", referencedColumnName = "id")
    @ManyToOne(optional = false)
    private Source source;

    @OneToMany(mappedBy = "note")
    private Collection<NoteFragmentAssignment> noteFragmentAssignmentByNoteCollection;

    // getters, setters etc.
}

Page.java
Code:
@Entity
@Table(name = "pages")
public class Page implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;

    @JoinColumn(name = "source", referencedColumnName = "id")
    @ManyToOne(optional = false)
    private Source source;

    @OneToMany(mappedBy = "page")
    private Collection<NoteFragment> noteFragmentByPageCollection;

    // getters, setters etc.
}

NoteFragment.java
Code:
@Entity
@Table(name = "note_fragments")
public class NoteFragment implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;

    @JoinColumns({
        @JoinColumn(name = "page", referencedColumnName = "id"),
        @JoinColumn(name = "source", referencedColumnName = "source")
    })
    @ManyToOne(optional = false)
    private Page page;

    @OneToMany(mappedBy = "fragment")
    private Collection<NoteFragmentAssignment> noteFragmentAssignmentByFragmentCollection;

    // getters, setters etc.
}

NoteFragmentAssignment.java
Code:
@Entity
@Table(name = "note_fragment_assignments")
public class NoteFragmentAssignment implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;

    @JoinColumns({
        @JoinColumn(name = "note", referencedColumnName = "id"),
        @JoinColumn(name = "note_source", referencedColumnName = "source")
    })
    @ManyToOne(optional = false)
    private Note note;

    @JoinColumns({
        @JoinColumn(name = "fragment", referencedColumnName = "id"),
        @JoinColumn(name = "fragment_source", referencedColumnName = "source")
    })
    @ManyToOne(optional = false)
    private NoteFragment fragment;

    // getters, setters etc.
}

On runtime, the Configuration.buildSessionFactory() method fails with the following exception:
Code:
org.hibernate.MappingException: Unable to find column with logical name: source in pages
    at org.hibernate.cfg.Ejb3JoinColumn.checkReferencedColumnsType(Ejb3JoinColumn.java:587)
    at org.hibernate.cfg.BinderHelper.createSyntheticPropertyReference(BinderHelper.java:258)
    at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:116)
    at org.hibernate.cfg.Configuration.processEndOfQueue(Configuration.java:1596)
    at org.hibernate.cfg.Configuration.processFkSecondPassInOrder(Configuration.java:1519)
    at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1420)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1844)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1928)
    at [...]

The exception is rewritten by Ejb3JoinColumn.checkReferencedColumnsType() from the exception thrown by Configuration$MappingsImpl.getPhysicalColumnName(). Indeed, Configuration.columnNameBindingPerTable entries don't contain the foreign keys (only the basic columns).

Is there a way to "cascade" the foreign keys in Hibernate? I am using Hibernate 4.3.4.Final.


Top
 Profile  
 
 Post subject: Re: foreign key referencing another foreign key column
PostPosted: Mon Mar 31, 2014 9:10 am 
Newbie

Joined: Sun Mar 30, 2014 5:06 pm
Posts: 2
I have found out that Configuration.secondPassCompile() processes classes in order of appearance in hibernate.cfg.xml. Moving Page class to the beginning leads to a new error:
Code:
org.hibernate.AnnotationException: referencedColumnNames(id, source) of [...].NoteFragmentAssignment.fragment referencing [...].NoteFragment not mapped to a single property
    at org.hibernate.cfg.BinderHelper.createSyntheticPropertyReference(BinderHelper.java:336)
    at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:116)
    at org.hibernate.cfg.Configuration.processEndOfQueue(Configuration.java:1557)
    at org.hibernate.cfg.Configuration.processFkSecondPassInOrder(Configuration.java:1478)
    at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1386)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1781)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1865)
    at [...]


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.