Joro Seksa
Joro Seksa

Reputation: 1583

JPA: Foreign key that is also a primary key mapping

I have been trying to solve this for whole day but no luck! Also i tried to read most of the tutorials on the net but as you all know they all are full of useless examples that do not reflect what you need in the real world.

So here is my situation:

The database:

table: vehicles(vehicleId, brand, model, devYear, regNumber) <-- vehicleId is the PrimaryKey

table: extras(vehicleId, allowSmoke, allowFood, allowDrinks, airConditioner) <-- vehicleId is a PK and a FK.

The point is that if i have a class Vehicle and a class TravelExtras which are mapped to the database i want the Vehicle class to have an attribute TravelExtras travelExtras and get and set methods.

Unfortunatelly no matter what i tried when i try to persist the object in the databse i get various errors.

Here is an illustration:

        EntityManagerFactory emfactory = Persistence.createEntityManagerFactory( "NaStopPU" );
        EntityManager entitymanager = emfactory.createEntityManager( );
        entitymanager.getTransaction( ).begin( );


        TravelExtra travelExtra = new TravelExtra();


        entitymanager.persist(travelExtra);


        Vehicle vehicle = new Vehicle(2L, "10152487958556242", "Mazda", "626", "334343", 2005, 4);  
        vehicle.setTravelExtra(travelExtra);

        entitymanager.persist(vehicle);



        entitymanager.getTransaction().commit();
        entitymanager.close( );

        emfactory.close( );

Any one knows what kind of annotations to use for this One to one case ?

Upvotes: 22

Views: 47729

Answers (4)

Sendi_t
Sendi_t

Reputation: 637

I know this is very old qs, but for completeness of your case you can just have (jpa 2.0)

@Entity
@Data
public class Vehicle implements Serializable{
   @Id
   @GeneratedValue
   private long vehicleId;
   .. //other props
}

@Entity
@Data
public class VehicleExtras implements Serializable{
         @Id
         @OneToOne (cascade = CASCADE.ALL)
         @MapsId
         @JoinColumn(name ="vehicleId")
         private Vehicle vehicle;

         @Column  
         private boolean allowSmoke; 
         ..// other props.
}

should share same pk/fk for VehicleExtra table

Upvotes: 10

Felipe Le&#227;o
Felipe Le&#227;o

Reputation: 925

Why don't you use an @Embedded object? When using an embedded object, you get the logical separation you desire in your code and keep your database compliant with Entity-Relational Normalization rules.

It's weird to think on a One-to-One relationship, because even though JPA/Hibernate allows it, all data should be stored in the same table, making you model simpler, while also simplifying queries and increasing database performance by removing the need for a Join operation.

When using Embedded objects you don't have to worry about mapping IDs and bizarre relations, since your ORM is capable of understanding that your just making a code separation, instead of demanding an actual relation of One-to-One between tables.

class Vehicle {
    @Id
    @Column(name = "ID")
    private long vehicleId; 
    @Column(name = "BRAND")
    private String brand; 
    @Column(name = "MODEL")
    private String model;
    @Column(name = "DEV_YEAR")
    private int devYear;
    @Column(name = "REG_NUMBER")
    private int regNumber;

    @Embedded
    private TravelExtra extras;

    // Constructor, getters and setters...

}

.

@Embeddable
class TravelExtras {

    @Column(name = "ALLOW_SMOKE")
    private boolean allowSmoke; 
    @Column(name = "ALLOW_FOOD")
    private boolean allowFood;
    @Column(name = "ALLOW_DRINKS")
    private boolean allowDrinks;
    @Column(name = "AIR_CONDITIONER")
    private boolean airConditioner;

    // Default Constructor, getters and setters...
}

Upvotes: 4

Chris Rice
Chris Rice

Reputation: 1430

The Java Persistence wikibook has a section called Primary Keys through OneToOne and ManyToOne Relationships which seems to indicate that what you want is possible.

If I'm reading it right, for your case, it would look something like:

class Vehicle {
    @Id
    @Column(name = "EXTRAS_ID")
    private long extrasId;

    @OneToOne(mappedBy="vehicle", cascade=CascadeType.ALL)
    private TravelExtra extras;
}

class TravelExtras {
    @Id
    @Column(name = "VEHICLE_ID")
    private long vehicleId;

    @OneToOne
    @PrimaryKeyJoinColumn(name="VEHICLE_ID", referencedColumnName="EXTRAS_ID")
    private Vehicle vehicle;

    public TravelExtras(Vehicle vehicle) {
        this.vehicleId = vehicle.getId();
        this.vehicle = vehicle;
    }
}

Note that one of your entities will need to make sure it has the same id as the other, which is accomplished in the example by the TravelExtras constructor requiring the Vehicle it is bound to.

Upvotes: 29

hiimjames
hiimjames

Reputation: 509

You can map your classes for example with Netbeans. It will generate annotations. The problem could be your dao layer. You have to persist objects in correct way. For example can't save travelExtra without Vehicle. Also be aware of owning side.

Upvotes: 0

Related Questions