I'm strugling with JPA. I tried several things but I can't figure out the right way to put the annotations.
What is want is like an Order/OrderLine relationship.
Thus:
Order( PK=orderId, fields=[...])
OrderLine (Pk1=orderId,Pk2=orderLineId, fields=[...])
Obviously, OrderLine.orderId refers to the 'Order' table.
What I functionally want to do is at least:
retrieve the Order with and without all orderlines. It should have a Set
retrieve an orderline by full PK, but without the associated Order.
retrieve a list of orderlines by orderId.
I only want these 2 tables and classes. nothing more nothing less.
I tried several things. Can anybody help me out with putting in the right annotations and members on these two classes?
Edit: what i've done so far.
Note that in this real example User=Order and UserRun=OrderLine. So, i am not interested in a seperate 'Run'-entity. Merely a UserRun as described by the Orderline.
#Entity
#Table(name = "user_runs")
public class UserRun {
#EmbeddedId
private UserRunKey id;
public UserRun(){};
public UserRun(String userName, String runUuid) {
this.id = new UserRunKey(userName, runUuid);
}
public String getUserName() {
return this.id.getUserName();
}
public String getRunUuid() {
return this.id.getRunUuid();
}
}
#Embeddable
class UserRunKey implements Serializable {
#Column(name = "username")
private String userName;
#Column(name = "run_uuid")
private String runUuid;
public UserRunKey(){};
public UserRunKey(String userName, String runUuid) {
this.runUuid = runUuid;
this.userName = userName;
}
public String getUserName() {
return userName;
}
public String getRunUuid() {
return runUuid;
}
}
This created a userruns/orderline table with the PK in the wrong way:
create table user_runs (run_uuid varchar(255) not null, username varchar(255) not null, primary key (run_uuid, username))
I want the primary key in reverse.
I want username as FK to User
I want a Set in my User-class.
When I do the following in my User-class:
#OneToMany
private Set<UserRun> userRuns;
It will create a
create table user_user_runs (user_username varchar(255) not null, user_runs_run_uuid varchar(255) not null, user_runs_username varchar(255) not null, primary key (user_username, user_runs_run_uuid, user_runs_username))
And that's something I definitely don't want! Once again, I don't want a Run-object (same as nobody's interested in a Line-class, from OrderLine)
I think I figured it out.
The UserRun/Orderline class:
#Entity
#Table(name = "user_runs")
public class UserRun {
#EmbeddedId
private UserRunKey id;
public UserRun(){};
public UserRun(String userName, String runUuid) {
this.id = new UserRunKey(userName, runUuid);
}
public String getUserName() {
return this.id.getUserName();
}
public String getRunUuid() {
return this.id.getRunUuid();
}
}
#Embeddable
class UserRunKey implements Serializable {
#Column(name = "username")
private String userName;
#Column(name = "run_uuid")
private String zrunUuid; //starts with a z, so the PK will be pk(username,run_uuid). Apparently, order in PK is determined from the variable names (alphabetic order)....
public UserRunKey(){};
public UserRunKey(String userName, String zrunUuid) {
this.zrunUuid = zrunUuid;
this.userName = userName;
}
public String getUserName() {
return userName;
}
public String getRunUuid() {
return zrunUuid;
}
}
In the userclass:
#OneToMany(mappedBy = "id.userName", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<UserRun> userRuns;
Unfortunately, there are 2 downsides:
I see that there are 2 queries executed instead of a Join on username. One to retrieve user, and 1 to retrieve the Set...
I needed to alter variablenames of the PK (compound/Embeddable). It seems there is no clean way to define the PK order. (Seriously?). Fortunately, the variable name is private, and not exposed by getter.
If anybody knows a cleaner way for these 2 issues. Let me know!
I think what you have to do is the following:
Because the primary key is compound key you need an ID class, as you already did:
#Embeddable
class OrderLinePK implements Serializable {
// you can use physical mapping annotations such as #Column here
#Column(name="...")
private Integer orderLineID;
// This is foreign key and the physical mapping should be done
// on the entity, and not here
private Integer orderID;
public OrderLinePK(){}
// getters + setters
// orverride equals() and hashCode() methods
}
Implement OrderLine entity
#Entity
public class OrderLine {
#EmbededId private OrderLinePK id;
#Mapsid("orderID")
#ManyToOne
#JoinColumn(name = "ORDER_ID", referencedColumn="ID")
private Order order;
// getters + setters ....
}
And the Order entity:
#Entity
public class Order {
#Id
private Integer id;
#OneToMany(fetch = FetchType.LAZY) // actually default by 1-to-n
private Coolection<OrderLine> orderLines;
// getters + setters ....
}
Related
Let's say I have two entities:
#Entity
public class Phone {
#Id
private Long id;
private String number;
}
#Entity
public class Person {
#Id
private Long id;
private String name;
}
The relationship between a person and a phone is one to one.
How could I access only the phone's number in the Person entity mapped by the phone's id
#Entity
public class Person {
#Id
private Long id;
private String name;
// ???
private String phoneNumber;
}
The reason for not mapping the whole entity is because in some more realistic entities there are too many properties.
I don't think you can, but something like this might be acceptable:
public class Person {
#OneToOne
#JoinColumn(name = "phone_id")
private Phone phone;
public String getPhoneNumber() {
return phone.getNumber();
}
}
Although you have mapped the whole object, not just the single property, you have only exposed the single property you want. The other stuff is hidden.
Alternatively, do it at the DB layer using a View:
create view person_with_phone as
select p.id, p.name,f.number
from person p
join phone f on f.id=p.phone_id
and then have an entity class to match the view. You'll need to turn off schema creation in your JPA implementation.
I have an EJB many-to-many (bi-directional) relation between classes (entity-classes) Person and Hobby. There are corresponding tables in the database, called PERSON and HOBBY, as well as a table PERSON_HOBBY for the many-to-many relationship.
As I will detail below, the problem is that whenever I try to persist a person with hobbies, I run into a Foreign Key constraint violation. This is because the entityManager tries to save new rows into PERSON_HOBBY that contain references to a person-entity with ID=0, which doesn’t exist in the PERSON table. I’ll come back to that later, but first I’ll show the relevant parts of the entity classes.
First, here is entity class Person:
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private String email;
#ManyToMany(cascade = {CascadeType.MERGE}, fetch = FetchType.EAGER)
/* Note: I used to have CascadeType.PERSIST in the above line as well, but
it caused "Detached object passed to persist" exceptions whenever I tried to
persist a person with hobbies. So I suppose I was right in deleting
CascadeType.PERSIST...? */
#JoinTable(name = "PERSON_HOBBY",
joinColumns = #JoinColumn(name="personId", referencedColumnName="id"),
inverseJoinColumns = #JoinColumn(name="hobbyId", referencedColumnName="id"))
private List<Hobby> hobbies = new ArrayList<Hobby>();
public List<Hobby> getHobbies () {
return hobbies;
}
public void setHobbies (List<Hobby> hobbies) {
this.hobbies = hobbies;
for(Hobby h:hobbies) { // this is to maintain bi-directionality
h.addPerson(this);
}
}
// other getters and setters omitted here.
Then entity class Hobby:
#Entity
public class Hobby {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(nullable = false)
private String description;
#ManyToMany(mappedBy = "hobbies", fetch = FetchType.EAGER)
private List<Person> persons;
public Hobby() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
// getter and setter for Description omitted here.
public List<Person> getPersons () {
return persons;
}
public void setPersons (List<Person> persons) {
this.persons = persons;
}
public void addPerson (Person p) {
this.persons.add(p);
}
}
I also have a stateless session bean, that’s shown here as far as relevant to the issue:
#Stateless
#Default
public class PersonRepositoryImpl implements PersonRepository {
#PersistenceContext
private EntityManager entityManager;
#Override
public Person create(Person p) {
entityManager.persist(p);
entityManager.flush();
return p;
}
#Override
public Person createPersonWithHobbies(Person p, List<Hobby>hobbyLijst) {
p = create(p); // I've also tried simply: create(p);
System.out.println("The newly assigned ID for the persisted
person is: " + p.getId());
// That last line **always** prints the person-ID as being 0 !!!!
p.setHobbies(hobbyLijst);
entityManager.merge(p); // This should save/persist the person's hobby's!
entityManager.flush();
return p;
}
}
Now from my servlet, I've been trying in two different ways. First, I tried calling method create(p) on the above session bean. That is, after creating a new Person instance p, setting all its non-relational fields, AND calling setHobbies on it (with a non-zero list of Hobby objects taken from the database), I called:
personRepo.create(p);
But this resulted in the Foreign Key (FK) exception:
INSERT on table 'PERSON_HOBBY' caused a violation of foreign key
constraint 'FK_EQAEPVYK583YDWLXC63YB3CXK' for key (0). The statement
has been rolled back.”
The FK-constraint mentioned here is the one in PERSON_HOBBY referring to PERSON.
The second way I tried was to make the following call from the servlet:
personRepo.createPersonWithHobbies(p, hobbyLijst);
where, just like before, p is the new person object; and hobbyLijst is that person's list of hobbies. And this resulted in the exact same FK-exception as the earlier call to personRepo.create(p).
Importantly, the println statement within method createPersonWithHobbies, calling getId() on the newly persisted person-object, ALWAYS gives that object's ID as being 0. Which I suppose does explain the FK-exception, since there's no person entity/row in the PERSON table with an ID of 0, nor is there supposed to be one. But of course the getId() call should not output 0. Instead, it should output the newly generated ID of the newly persisted person entity. (And yes, it IS persisted correctly in the PERSON tabel, with a correctly generated ID>0. So the correct ID is there in the PERSON-table - it just seems to be invisible to the entityManager and/or the container.)
Thanks.
I have two classes, monitoringExpression and reportTrigger, with a one-to-many relationship. Hibernate is attempting to persist duplicate reportTriggers from the collection held by the monitoringExpression class. The first insert into the reportTrigger collection works, but subsequent inserts fail with a unique constraint violation because hibernate tries to persist the same reportTrigger twice. This is quite similar to a known hibernate bug (Hibernate inserts duplicates into a #OneToMany collection); however, in this case, we are not using a lazy collection. Here is the relevant code:
MonitoringExpression.Class
#Audited
#Entity
#Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class MonitoringExpression extends GeneratedIdXmlObject{
private String name;
private DeterminantDefinition determinantDefinition;
private String valueExpression;
private String testExpression;
private String messageExpression;
private String messageSeverity;
private boolean setExitStatusWhenTrue;
protected SortedSet<MonitoringExpressionAttribute> attributes = new TreeSet<MonitoringExpressionAttribute>();
private String color;
private Set<ReportTrigger> reportTriggers = new HashSet<ReportTrigger>();
.
.
.
#OneToMany(mappedBy="monitoringExpression",orphanRemoval=true,cascade={CascadeType.ALL},fetch=FetchType.EAGER)
#Sort(type=SortType.NATURAL)
#Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public SortedSet<MonitoringExpressionAttribute> getAttributes() {
return attributes;
}
public void setAttributes(SortedSet<MonitoringExpressionAttribute> attributes) {
this.attributes = attributes;
}
ReportTrigger.Class
#Audited
#Table(name="ReportTrigger")
#Entity
#Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class ReportTrigger extends GeneratedIdXmlObject{
private String name;
private String description;
private TriggerableReport report;
private Frequency burstPeriodSize;
private String periodStartExpression;
private String periodEndExpression;
private Set<ReportTriggerParameterMapping> parameterMappings = new HashSet<ReportTriggerParameterMapping>();
private MonitoringExpression monitoringExpression;
#XmlIDREF
#ManyToOne
#Audited
#GraphProcessorOverride(process=false,recurse=false)
#NaturalId(mutable=true)
public TriggerableReport getReport() {
return report;
}
public void setReport(TriggerableReport report) {
this.report = report;
}
#Embedded
public Frequency getBurstPeriodSize() {
return burstPeriodSize;
}
public void setBurstPeriodSize(Frequency burstPeriodSize) {
this.burstPeriodSize = burstPeriodSize;
}
#Audited
#OneToMany(mappedBy="reportTrigger",orphanRemoval=true,cascade={CascadeType.ALL})
#Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public Set<ReportTriggerParameterMapping> getParameterMappings() {
return parameterMappings;
}
.
.
.
#XmlIDREF
#ManyToOne
#GraphProcessorOverride(process=false,recurse=false)
#NaturalId(mutable=true)
public MonitoringExpression getMonitoringExpression() {
return monitoringExpression;
}
public void setMonitoringExpression(MonitoringExpression monitoringExpression) {
this.monitoringExpression = monitoringExpression;
}
As far as I can tell, we're not doing anything out of the ordinary to the reportTrigger collection (and we obviously cannot be adding the same tigger twice to a set). Has anyone seen anything like this? Thanks
Hibernate 3.6.10
Java 8
I think can do some checking as below
- Check unique constraint violation error you got is on which field ? It can be on Primary key column but also can any unique columns.
- If the violation is with your primary key then while your collection is a set then it can be 2 different TriggerableReport but share the same key value.
I am trying to develop a web application and I was wondering if there is a way to utilize a foreign key without writing a lot of code.
My Trainees.java
#Entity
public class Trainees {
#Id
#GeneratedValue
private int traineesID;
private int groupsID;
#ManyToOne
#JoinColumn(name = "status_trainee")
private String status_TraineeID;
private int customersID;
private String name;
private String surname;
private String phoneDetails;
private String email;
public Trainees(){
}
public Trainees(String name, String surname, String phoneDetails, String email, int id, int groupsID, String status_TraineeID, int customersID) {
super();
this.name = name;
this.surname = surname;
this.email = email;
this.phoneDetails = phoneDetails;
this.groupsID = groupsID;
this.status_TraineeID = status_TraineeID;
this.customersID = customersID;
}
//getters and setters
#Override
public boolean equals(Object object) {
if (object instanceof Trainees){
Trainees contact = (Trainees) object;
return contact.traineesID == traineesID;
}
return false;
}
#Override
public int hashCode() {
return traineesID;
}
}
Status_Trainee.java
#Entity
public class Status_Trainee {
#Id
#GeneratedValue
private int status_traineeID;
private String value;
public Status_Trainee(){
}
public Status_Trainee(String value, int id) {
super();
this.value = value;
}
//getters and setters
#Override
public boolean equals(Object object) {
if (object instanceof Status_Trainee){
Status_Trainee value = (Status_Trainee) object;
return value.status_traineeID == status_traineeID;
}
return false;
}
#Override
public int hashCode() {
return status_traineeID;
}
}
Error: Caused by: org.hibernate.AnnotationException: #OneToOne or #ManyToOne on uaiContacts.model.Trainees.status_TraineeID references an unknown entity: String
So my aim is that using the Trainees table and class, I could retrieve the value of Status_Trainee table using the foreign key. For example: if foreign keys ID is 2, then it would retrieve a string from status_trainee table where primary key would match the foreign key ID.
I am using models, controlers, hibernate and angularjs to display to the view, I don't really want to pass the table through all this, I thought using something like ManyToOne or JoinColumns would retrieve the value?
Thanks for all the help!
You should add a reference to StatusTrainee in Trainee and annotate that with OneToMany, ManyToOne or OneToOne. Depending on which kind of relationship you will need a list of StatusTrainee or just a StatusTrainee.
Tip: dont use underscores in class names.
First of all, it is not recommended to use "_" in a class name when using hibernate. Hibernate uses underscores when accessing foreignKeys. So Lets Say you rename your class to: TraineeStatus and the id change it to traineeStatusId..
Secondly, You can use the Hibernate annotations for what you need. but first you need to know how the relation is:
#OneToMany : One Trainee can have lots of statuses
#ManytoOne : Many trainees can have the same status
#OneToOne : one Trainee Can only have one status and the other way around.
Try this:
#Entity
public class Trainees {
#Id
#GeneratedValue
private int traineesID;
private int groupsID;
#OneToOne
private TraineeStatus status;
private int customersID;
private String name;
private String surname;
private String phoneDetails;
private String email;
...
You can change the #OneToOne for the one you need..
Remember that hibernate will try to map this in your Trainees mysql table as status_traineeStatusId, so if you have this column (as an integer) at your trainess table you are done :)..
That is it..
Hope it helps
Can anyone tell me whether Hibernate supports associations as the pkey of an entity? I thought that this would be supported but I am having a lot of trouble getting any kind of mapping that represents this to work. In particular, with the straight mapping below:
#Entity
public class EntityBar
{
#Id
#OneToOne(optional = false, mappedBy = "bar")
EntityFoo foo
// other stuff
}
I get an org.hibernate.MappingException: "Could not determine type for: EntityFoo, at table: ENTITY_BAR, for columns: [org.hibernate.mapping.Column(foo)]"
Diving into the code it seems the ID is always considered a Value type; i.e. "anything that is persisted by value, instead of by reference. It is essentially a Hibernate Type, together with zero or more columns." I could make my EntityFoo a value type by declaring it serializable, but I wouldn't expect this would lead to the right outcome either.
I would have thought that Hibernate would consider the type of the column to be integer (or whatever the actual type of the parent's ID is), just like it would with a normal one-to-one link, but this doesn't appear to kick in when I also declare it an ID. Am I going beyond what is possible by trying to combine #OneToOne with #Id? And if so, how could one model this relationship sensibly?
If the goal is to have a shared primary key, what about this (inspired by the sample of Java Persistence With Hibernate and tested on a pet database):
#Entity
public class User {
#Id
#GeneratedValue
private Long id;
#OneToOne(cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private Address shippingAddress;
//...
}
This is the "parent" class that get inserted first and gets a generated id. The Address looks like this:
#Entity
public class Address implements Serializable {
#Id #GeneratedValue(generator = "myForeignGenerator")
#org.hibernate.annotations.GenericGenerator(
name = "myForeignGenerator",
strategy = "foreign",
parameters = #Parameter(name = "property", value = "user")
)
#Column(name = "ADDRESS_ID")
private Long id;
#OneToOne(mappedBy="shippingAddress")
#PrimaryKeyJoinColumn
User user;
//...
}
With the above entities, the following seems to behave as expected:
User newUser = new User();
Address shippingAddress = new Address();
newUser.setShippingAddress(shippingAddress);
shippingAddress.setUser(newUser); // Bidirectional
session.save(newUser);
When an Address is saved, the primary key value that gets inserted is the same as the primary key value of the User instance referenced by the user property.
Loading a User or an Address also just works.
Let me know if I missed something.
PS: To strictly answer the question, according to Primary Keys through OneToOne Relationships:
JPA 1.0 does not allow #Id on a OneToOne or ManyToOne, but JPA 2.0 does.
But, the JPA 1.0 compliant version of Hibernate
allows the #Id annotation to be used on a OneToOne or ManyToOne mapping*.
I couldn't get this to work with Hibernate EM 3.4 though (it worked with Hibernate EM 3.5.1, i.e. the JPA 2.0 implementation). Maybe I did something wrong.
Anyway, using a shared primary key seems to provide a valid solution.
Yes that is possible.
Look at the following example using Driver and DriverId class as id for Driver.
#Entity
public class Drivers {
private DriversId id; //The ID which is located in another class
public Drivers() {
}
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "personId", column = #Column(name = "person_id", nullable = false))})
#NotNull
public DriversId getId() {
return this.id;
}
//rest of class
}
Here we are using personId as the id for Driver
And the DriversId class:
//composite-id class must implement Serializable
#Embeddable
public class DriversId implements java.io.Serializable {
private static final long serialVersionUID = 462977040679573718L;
private int personId;
public DriversId() {
}
public DriversId(int personId) {
this.personId = personId;
}
#Column(name = "person_id", nullable = false)
public int getPersonId() {
return this.personId;
}
public void setPersonId(int personId) {
this.personId = personId;
}
public boolean equals(Object other) {
if ((this == other))
return true;
if ((other == null))
return false;
if (!(other instanceof DriversId))
return false;
DriversId castOther = (DriversId) other;
return (this.getPersonId() == castOther.getPersonId());
}
public int hashCode() {
int result = 17;
result = 37 * result + this.getPersonId();
return result;
}
}
You can do this by sharing a primary key between EntityFoo and EntityBar:
#Entity
public class EntityBar
{
#Id #OneToOne
#JoinColumn(name = "foo_id")
EntityFoo foo;
// other stuff
}
#Entity
public class EntityFoo
{
#Id #GeneratedValue
Integer id;
// other stuff
}
You have to use #EmbeddedId instead of #Id here.
And EntityFoo should be Embeddable.
Another way is to put an integer, and a OneToOne with updateble and instertable set to false.