Is it possible to create a basic FK relationship in JPA without involving the full entity target object?
As an example, imagine I have an entity:
#Entity(name = "Mechanic")
public class Mechanic {
#Id
private Long id;
//...
and a Car that I want to reference a Mechanic.id:
#Entity(name = "Car")
public class Car {
//...
#NotNull
private Long mechanic_id;
From an Object perspective, this would be a unidirectional, one to one relationship with the Car requiring a Mechanic.id and the Mechanic not needing any back reference to Car.
All I want out of this is to store the Mechanic.id ONLY. For the purposes of this question it is not useful to have a #OneToOne (or #OneToMany etc) relationship with the entity reference, I'm explicitly trying to avoid that but still retain the underlying integrity that a FK will provide.
JPA 2 and I'm using EclipseLink.
Related
Some JPA provider like Hibernate uses Proxy to handle lazy initialization. Consider the following example:
#Entity
public class Person {
#Id
private Long id;
#ManyToOne(fetch=FetchType.LAZY)
private House house;
}
#Entity
public class House {
#Id
private Long id;
#Embedded
private Address address;
}
When fetching a Person entity, its house property is set to a Proxy (lazy).
Person person = em.find(Person.class, 1);
House house = person.getHouse(); // Proxy
if (house == null)
System.out.println("has no house);
else
System.out.println("has a house");
If the person does not have a house, the person object has a Proxy of house (not null). The code above will print wrong message. Is this an issue for JPA proxy?
As a matter of fact, I'm surprised you're facing this issue. According to this question: Making a OneToOne-relation lazy, lazy optional many-to-one associations should work just fine; it is the one-to-one associations that cause problems. The issue here is that without enhancement, Hibernate cannot automagically turn the proxy into a null reference. Are you actually seeing this behavior in Hibernate?
In any case, you should be able to resolve the issue by enabling enhancement. This way, Hibernate is able to overwrite the getter method to return null if the initialized proxy does not represent a valid House. Not sure how the issue is resolved by other providers, though.
I was thinking that I understand the relations. But now.. Im not sure. I have a problem to add correct annotations.
I have classes Members and Relations.
In Relations
private GT_Member Mother;
private GT_Member Father;
private List<GT_Member> children;
One Member can be in several Relations as Mother or Father (reference to his sex) but he can be only in One relation as child.
I thought annotate Mother and Father as #OneToMany.
But I'm not sure if I can annotate List as #OneToOne ??
This seems like a problem in modeling the correct entity-relationship model for your database schema and visualizing your ORM (Object Relationship Model).
Rather than starting with classes Members and Relations, please first see what are the dominant data-entities in your system. And how would they be related to each other.
Personally I do not think Relation would be a good JPA entity.
Member looks more like a good entity and could embody the relations
Assuming one Father and one Mother, One to Many seems wrong but as a father or mother can have many children, the correct annotation should be #ManyToOne.
Children is definitely OneToMany, and yes you can annotate the List children as #OneToMany.
Member could have the following properties:
#Entity
public class Member implements Serializable{
#ManyToOne
private Member mother;
#ManyToOne
private Member father;
#OnetoMany
private List<Member> children;
}
This solves both your use cases and in this simple example Relation class is not needed.
hope this helps.
Employment of Relation entity is because I want to save information about status of relation. Donc I will store information about all married etc. Entity relation has other fields like type (neutral, married, fiance etc...).
I have a m:n relationship book - borrow - user, the borrow is the join table.
The tables are given (can not be changed):
on one side they are used by jdbc app as well.
on the other side i would like to use them via jpa
book(book_id) - borrow(book_id,used_id) - user(user_id)
used jpa annotations:
User:
#OneToMany(targetEntity=BorrowEntity.class, mappedBy="user")
#JoinColumn(name="USER_ID", referencedColumnName="USER_ID")
private List<BorrowEntity>borrowings;
Book:
#OneToMany(targetEntity=BorrowEntity.class, mappedBy="book")
#JoinColumn(name="BOOK_ID", referencedColumnName="BOOK_ID")
private List<BorrowEntity>borrowings;
My problem is that by the settings above it adds some extra (undesired) fields to the borrow table:
'user_USER_ID' and 'book_BOOK_ID'
How can I configure the jpa annotations to keep just Borrow:user_id,book_id which is enough the many to one ?
Take a look at the picture which tells more:
First of all, since the borrow table is a pure join table, you don't need to map it at all. All you need is a ManyToMany association using this borrow table as JoinTable.
#ManyToMany
#JoinTable(name = "borrow",
joinColumns = #JoinColumn(name = "USER_ID"),
inverseJoinColumns = #JoinColumn(name = "BOOK_ID"))
private List<Book> borrowedBooks;
...
#ManyToMany(mappedBy = "borrowedBooks")
private List<User> borrowingUsers;
If you really want to map the join table as an entity, then it should contain two ManyToOne associations (one for each foreign key). So the following is wrong:
#OneToMany(targetEntity=BorrowEntity.class, mappedBy="user")
#JoinColumn(name="USER_ID", referencedColumnName="USER_ID")
private List<BorrowEntity>borrowings;
Indeed, mappedBy means: this association is the inverse side of the bidirectional OneToMany/ManyToOne association, which is already mapped by the field user in the BorrowEntity entity. Please see the annotations on this field to know how to map the association.
So the #JoinColumn doesn't make sense. It's in contradiction with mappedBy. You just need the following:
#OneToMany(mappedBy="user")
private List<BorrowEntity>borrowings;
The targetEntity is also superfluous, since it's a List<BorrowEntity>: JPA can infer the target entity from the generic type of the list.
Isn't it possible to map xml to jpa entities using JAXB? Will Eclipselink Moxy be helpful?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
Yes you can map JPA entities to XML, and the following are some ways that EclipseLink JAXB (MOXy) makes this easier.
1. Bidirectional Mappings
Customer
import javax.persistence.*;
#Entity
public class Customer {
#Id
private long id;
#OneToOne(mappedBy="customer", cascade={CascadeType.ALL})
private Address address;
}
Address
import javax.persistence.*;
import org.eclipse.persistence.oxm.annotations.*;
#Entity
public class Address implements Serializable {
#Id
private long id;
#OneToOne
#JoinColumn(name="ID")
#MapsId
#XmlInverseReference(mappedBy="address")
private Customer customer;
}
For More Information
http://blog.bdoughan.com/2010/07/jpa-entities-to-xml-bidirectional.html
http://bdoughan.blogspot.com/2010/08/creating-restful-web-service-part-25.html
2. Mapping Compound Key Relationships
We normally think of mapping a tree of objects to XML, however JAXB supports using the combination of #XmlID/#XmlIDREF to map relationship between nodes representing a graph. The standard mechanism is one key, to one foreign key. JPA supports the concept of composite keys and so does MOXy using #XmlKey and #XmlJoinNodes (similar to #XmlJoinColumns in JPA).
Employee
#Entity
#IdClass(EmployeeId.class)
public class Employee {
#Id
#Column(name="E_ID")
#XmlID
private BigDecimal eId;
#Id
#XmlKey
private String country;
#OneToMany(mappedBy="contact")
#XmlInverseReference(mappedBy="contact")
private List<PhoneNumber> contactNumber;
}
PhoneNumber
#Entity
public class PhoneNumber {
#ManyToOne
#JoinColumns({
#JoinColumn(name="E_ID", referencedColumnName = "E_ID"),
#JoinColumn(name="E_COUNTRY", referencedColumnName = "COUNTRY")
})
#XmlJoinNodes( {
#XmlJoinNode(xmlPath="contact/id/text()", referencedXmlPath="id/text()"),
#XmlJoinNode(xmlPath="contact/country/text()", referencedXmlPath="country/text()")
})
private Employee contact;
}
For More Information
http://wiki.eclipse.org/EclipseLink/Examples/MOXy/JPA/CompoundPrimaryKeys
3. MOXy allows for Composite and Embedded Keys
JPA can also use embedded key classes to represent composite keys. MOXy also supports this style of composite keys.
For More Information
http://wiki.eclipse.org/EclipseLink/Examples/MOXy/JPA/EmbeddedIdClass
4. EclipseLink JAXB (MOXy) and EclipseLink JPA Have Shared Concepts
EclipseLink provides both JAXB and JPA implementations that share a common core. This means that they share many of the same concepts, such as:
Virtual Access Methods
EclipseLink supports the concept of virtual properties. This is useful when creating a multi-tenant application where you want per-tenant customizations. This concept is upported in both EclipseLink's JPA and JAXB implementations.
http://blog.bdoughan.com/2011/06/moxy-extensible-models-multi-tenant.html
http://wiki.eclipse.org/EclipseLink/Examples/JPA/Extensibility
I'm asking and answering my own question, but i'm not assuming i have the best answer. If you have a better one, please post it!
Related questions:
How to set a backreference from an #EmbeddedId in JPA
hibernate mapping where embeddedid (?)
JPA Compound key with #EmbeddedId
I have a pair of classes which are in a simple aggregation relationship: any instance of one owns some number of instances of the other. The owning class has some sort of primary key of its own, and the owned class has a many-to-one to this class via a corresponding foreign key. I would like the owned class to have a primary key comprising that foreign key plus some additional information.
For the sake of argument, let's use those perennial favourites, Order and OrderLine.
The SQL looks something like this:
-- 'order' may have been a poor choice of name, given that it's an SQL keyword!
create table Order_ (
orderId integer primary key
);
create table OrderLine (
orderId integer not null references Order_,
lineNo integer not null,
primary key (orderId, lineNo)
);
I would like to map this into Java using JPA. Order is trivial, and OrderLine can be handled with an #IdClass. Here's the code for that - the code is fairly conventional, and i hope you'll forgive my idiosyncrasies.
However, using #IdClass involves writing an ID class which duplicates the fields in the OrderLine. I would like to avoid that duplication, so i would like to use #EmbeddedId instead.
However, a naive attempt to do this fails:
#Embeddable
public class OrderLineKey {
#ManyToOne
private Order order;
private int lineNo;
}
OpenJPA rejects the use of that as an #EmbeddedId. I haven't tried other providers, but i wouldn't expect them to succeed, because the JPA specification requires that the fields making up an ID class be basic, not relationships.
So, what can i do? How can i write a class whose key contains #ManyToOne relationship, but is handled as an #EmbeddedId?
I don't know of a way to do this which doesn't involve duplicating any fields (sorry!). But it can be done in a straightforward and standard way that involves duplicating only the relationship fields. The key is the #MapsId annotation introduced in JPA 2.
The embeddable key class looks like this:
#Embeddable
public class OrderLineKey {
private int orderId;
private int lineNo;
}
And the embedding entity class looks like this:
#Entity
public class OrderLine{
#EmbeddedId
private OrderLineKey id;
#ManyToOne
#MapsId("orderId")
private Order order;
}
The #MapsId annotation declares that the relationship field to which it is applied effectively re-maps a basic field from the embedded ID.
Here's the code for OrderId.