views:

142

answers:

3

I have a simple question regarding Entity declaration in JPA. I have an entity with 2 foreign keys, which are not null and form an uniqueConstraint. First I was thinking about a composite key, composed of the two foreign keys, but I heard that this is a legacy design, and not the recommended way of designing new tables.

So I am interested if Hibernate/JPA can automatically generate id, based on the two foreign keys. Let's say I have the following Entity:

@Entity
public class Foo {
  @ManyToOne
  private Bar bar;
  private int i;
}

(I omitted not null and uniqueConstraint tags to make the code more readable)

I know I can simply add an id field, with GeneratedValue, and let my DB generate the key (in my example MySQL with auto_increment), but this seems inefficient to me as it involves querying the database, and asking it to generate the unique id value.

Is there a way of generating an id, which is not composite (i.e. of type int or long), based on the id of the "Bar" class, and value of the integer "i", since it those two values already form a unique constraint?

+1  A: 

I think the 'ineffeciency' is so minor that in 99.99% of case it can be ignored.

For DB supporting auto increment column, there is no extra round trip for asking it to generate the ID. For DB that does not support auto increment column (e.g. Oracle), Hibernate have some optimization done to reduce DB access for ID generation (e.g. get the sequence value, multiply by 50 and use the next 50 values from the result as new entities' ID)

Adrian Shum
+1  A: 

I think you should rethink your design here; it probably makes more sense to have a composite key, or better an Id.

The rationale of generating primary key values out of foreign keys can backfire. This is because primary keys are not meant to be modified - what if one of the foreign key values change? Should the primary key value be regenerated? And should column references in other tables be modified as a result?. In any case, JPA requires that primary keys not be changed, so you're better off having a natural or a surrogate key.

The effort in writing a generator, is better spent in ensuring that the model is correct.

Vineet Reynolds
Those foreign keys actually cannot be changed.
leden
+3  A: 

Hi ,

You may want to check out Chapter7 of "Java Persistence with Hibernate".

You can model the composite key as an Embeddable:

import javax.persistence.*;
import java.io.Serializable;

@Entity
public class Foo {

    @Embeddable
    public static class Id implements Serializable {
        @Column(name = "bar_id_col")
        private Long barId;

        @Column(name = "i_col")
        private int i;

        public Id() {
        }

        public Id(Long barId, int i) {
            this.barId = barId;
            this.i = i;
        }

        @Override
        public boolean equals(final Object o) {
            if (this == o) {
                return true;
            }
            if (!(o instanceof Id)) {
                return false;
            }

            final Id id = (Id) o;

            if (i != id.i) {
                return false;
            }
            if (barId != null ? !barId.equals(id.barId) : id.barId != null) {
                return false;
            }

            return true;
        }

        @Override
        public int hashCode() {
            int result = barId != null ? barId.hashCode() : 0;
            result = 31 * result + i;
            return result;
        }
    }

    @EmbeddedId
    private Id id = new Id();

    @ManyToOne
    @JoinColumn(name = "bar_id_col", insertable = false, updatable = false)
    private Bar bar;

    private int i;

    public Foo() {
    }

    public Foo(Bar bar, int i) {
        // set fields
        this.Bar = bar;
        this.i=i;
        // set identifier values
        this.id.barId = bar.getId();
        this.id.i = i;
    }

}

Here I assume Bar looks like:

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Bar {

    @Id
    Long id;

    public Long getId() {
        return id;
    }

    public void setId(final Long id) {
        this.id = id;
    }
}

Notice that this maps the bar_id_col twice. This is the reason for insertable = false, updatable = false in the second reference.

It's tricky, but if you really want to do it like this, it's possible.

Good luck, J.

Jan
Thanks, man! That's exactly what I need.
leden