JPA #OneToMany Sets and unique contstaints - java

I have the following scenario:
I have a system full of users. There is a desire to run contests for users who log into the site over a week or so. Therefore I needed to create a new Contest object which would contain both the entries and the winners.
I created something like this:
private Set<User>;
#OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY)
#JoinTable(name="contest_entries",
joinColumns = {#JoinColumn(name = "contest_fk", referencedColumnName="contest_id")},
inverseJoinColumns = {#JoinColumn(name = "user_fk", referencedColumnName="id")})
public Set<User> getEntries() {
return entries;
}
The idea being that a Contest can have multiple entries. Seems simple. It generates the following:
create table contest (contest_id numeric(19,0) identity not null, primary key (contest_id));
create table contest_entries (contest_fk numeric(19,0) not null, user_fk numeric(19,0) not null, primary key (contest_fk, user_fk));
alter table contest_entries add constraint FK7FBD0C65C4F120C2 foreign key (contest_fk) references contest;
alter table contest_entries add constraint FK7FBD0C656ABC75E3 foreign key (user_fk) references user_profile;
However, once a contest is over, the desire is to run another contest. When I attempt to create a new contest and have one of users who had entered previously enter again, I get a unique key constraint. Looking at the table DDL, it makes sense that it does.
So, in essence, I can't run two contests at the same time. I would also lose history of people entering the contests. That's not going to work. I need to be able to run two contests at once with the same users in different contests.
I'm new to JPA, so I have to believe I'm missing something obvious. The system already has a user table and it's populated full of users. There is a desire not to change that table structure. Any ideas on how to solve this? If it matters, the persistence implementation is Hibernate.

You actualy have a many-to-many relationship. So you should use the #ManyToMany annotation.

Related

How do i create a relationship with an extra attribute such as boolean?

I'm in a state of designing before coding this problem.
I do have some ideas in mind but I don't think they will be valid.
Let say, I have project class that has department_id as a foreign key and pid as generated id.
I also have employees that have their own unique id and info.
I would like to create a relationship between that employee and a student, this could be easily done in Employee class like this:
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "emp_projects",
joinColumns = #JoinColumn(name = "emp_id", referencedColumnName = "uid" ),
inverseJoinColumns = #JoinColumn(name = "project_id", referencedColumnName = "pid"))
private Set<Project> projects = new HashSet<>();
Hence the problem in that code, how do I add another attribute hasCompleted as a boolean to see if that employee submitted his project or not?
I can't within JoinTable.
I've tried to search a lot of articles and there's none that explains how to approach this. Maybe I missed some, I would really appreciate the help.
How do I add another attribute hasCompleted as a boolean to see if that employee submitted his project or not?
Your code mentions #OneToMany to I assume there's only one employee/student possible for each project. In that case you would add the property to Project to indicate that the project has been submitted (by its owner).
If the relation was actually a many-to-many one, i.e. an employee can work on multiple projects and a project can be worked on by multiple employees (more likely in the real world) then you'd need a mapping table anyway since neither Project nor Employee could take that data in a relational database.
However, where to actually put it would also depend on the meaning:
Project has been completed -> put it into Project
Employee has completed their part of a project -> put it into the mapping table

Hibernate embeddable list mapping with identifier

I have a Person entity with an embeddable Address and there's a one-to-many relation between them (a person can have multiple addresses). The current mapping is something like this:
#Embeddable
public class Address {
// ... attributes
}
#Entity
public class Person {
#ElementCollection(fetch = FetchType.EAGER)
#JoinTable(name = "Person_addresses", joinColumns = #JoinColumn(name = "personid")
)
/*
Attribute ovverrides with annotations
*/
private java.util.Set<Address> addresses = new java.util.HashSet<Address>();
}
Using this annotation means that in the database I have a Person_addresses table which contains all the address attributes and a personid. But it also means that if I have a person with an address list and I update the address list, Hibernate deletes all the related records and inserts them (the modified ones) again.
As far as I know there's a way to have a primary key in this table for each record - in this case hibernate can decide which item of the list needs to be updated. So my question is, how can I map an embeddable list with identifiers in the joining table? (I hope it's understandable what I want:)).
http://en.wikibooks.org/wiki/Java_Persistence/ElementCollection#Primary_keys_in_CollectionTable
The JPA 2.0 specification does not provide a way to define the Id in
the Embeddable. However, to delete or update a element of the
ElementCollection mapping, some unique key is normally required.
Otherwise, on every update the JPA provider would need to delete
everything from the CollectionTable for the Entity, and then insert
the values back. So, the JPA provider will most likely assume that the
combination of all of the fields in the Embeddable are unique, in
combination with the foreign key (JoinColumn(s)). This however could
be inefficient, or just not feasible if the Embeddable is big, or
complex. Some JPA providers may allow the Id to be specified in the
Embeddable, to resolve this issue. Note in this case the Id only needs
to be unique for the collection, not the table, as the foreign key is
included. Some may also allow the unique option on the CollectionTable
to be used for this. Otherwise, if your Embeddable is complex, you may
consider making it an Entity and use a OneToMany instead.
So thats it, it can't be done.
As maestro's reply implies, the only portable solution is to convert this to use an entity and a one-to-many.
That said, Hibernate has a non-spec feature called an "id bag" which allows you to map a basic or embeddable collection with an identifier for each row, thereby giving you the efficient updates you want:
#Entity
public class Person {
#CollectionId( columns={"address_id"}, type="int", generator="increment" )
#ElementCollection(fetch = FetchType.EAGER)
#JoinTable(name = "Person_addresses", joinColumns = #JoinColumn(name = "personid"))
private java.util.List<Address> addresses = new java.util.ArrayList<Address>();
}
Notice the switch from Set to List however. Also notice the generated table structure... looks an awful lot like an entity ;)

How to automatically remove child entities?

I'd like to remove entities from my table and have it auto-removed any entities that are childs of it.
Example:
class User {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval=true)
#OnDelete(action = OnDeleteAction.CASCADE)
List<Address> addresses;
}
When I remove a User that has no address, everything works fine. Also removing an address without removing the user works.
But: If I try to remove a user that has still some addresses, I'm getting org.hsqldb.HsqlException:
integrity constraint violation: foreign key no action; FK_ADDRESS_USER_ID table: ADDRESS
What might be wrong here?
Or is this not supported and I have to explicitly remove all contained Address objects first before deleting a user?
I believe you have a problem with a foreign key constraint. Use a DB tool like Aqua Data Studio or similar (you can probably also do this in your IDE, in Eclipse - Data Source Explorer view), to show the create script for your ADDRESS table. It should contain something like this:
ALTER TABLE TESTSCHEMA.ADDRESS
ADD CONSTRAINT FK1ED033D4E91AAFD9
FOREIGN KEY(FK_ADDRESS_USER_ID)
REFERENCES TESTSCHEMA.USER(ID)
ON DELETE CASCADE
The point being the ON DELETE CASCADE part in your case. If this is missing or is different, that probably is what is causing the problem. If the table is auto-generated by Hibernate, this constraint should be valid, but keep in mind that there are differences between databases. It could be that the table was generated before the Hibernate's #OnDelete annotation was added, so now you are getting the "foreign key no action" integrity constraint violation.
Not related to the issue, but do note that orphanRemoval=true will try to remove the address from the database, when it is removed from the collection in the User entity.
Also, check this out for details on Hibernate’s support for database ON DELETE CASCADE constraint.

Why does nullable = false force a FK to be put in on insert, but without it it does an update to set the FK in JPA/Hibernate

Using JPA annotations and hibernate we ran in to an issue recently with a unidirectional onetomany mapping that looked like this:
#Entity
public class FooOrder extends AbstractEntity{
#OneToMany(cascade= CascadeType.ALL,orphanRemoval = true)
#JoinColumn(name = "fooOrderId")
private List<FooItem> fooItems = new ArrayList<FooItem>();
public void addFooItem(foo item properties here)
{
fooItems.add(fooItem);
}
}
#Entity
public class FooItem extends AbstractEntity {
SomeRandomStuffButNoLinkToParent
}
The test code was basically this:
FooOrder fooOrder = new FooOrder(stuff here);
fooOrder.addFooItem(foo item properties here);
fooOrder = fooOrderRepository.save(fooOrder);
When we run tests on this we get sql that looks something like this:
insert FooOrder(columns here)
insert FooItem(columns here missing the FK to FooOrder)
update FooItem set FooOrderFK to proper key.
but if I set #JoinColumn(name = "activeOrderId", nullable = false) then my sql looks something like this:
insert FooOrder(columns here)
insert FooItem(columns here with FK to FooOrder)
Why does hibernate set the FK through an update if it's nullable, but set it in the insert when it's not nullable?
Well when the foreign key is not null able
insert FooOrder(columns here)
insert FooItem(columns here with FK to FooOrder)
Is the only way to actually execute the inserts. So the real question is why this is not always done this way.
My take on this is that the way using updates works in times when the other would not ever work.
Let assume we have some kind of circular foreingn key relationship.
A has a foreign key to B
B has a foreign key to A
if none of this foreign keys is nullable there might be no way to insert this.
You can not insert A first because the foreign key to B can not be null.
Same goes for B.
Also note that my example might be over simplyfied. This circular foreign key can be caused by any number of tables.
So first inserting the data and then adding the foreign keys is just the way that works in this more complicated scenario. Your not null constraint forces Hibernate to take another way. For the default case first inserting then adding the keys is just he better idear.

Lazy loading problem with one to one mapping

I have 2 very simple POJOs and i have just one to one mapping between them.
contact.java
comment.java -- it has foreign key column to the contact.java
the code i have written is below.
contact.java
#OneToOne(optional= false,cascade = CascadeType.ALL, mappedBy="contact", fetch=FetchType.LAZY)
#org.hibernate.annotations.LazyToOne(org.hibernate.annotations.LazyToOneOption.NO_PROXY)
private Comment mComment;
comment.java
#OneToOne(fetch = FetchType.LAZY,cascade=CascadeType.ALL )
#JoinColumn(name="EW_CNTC_ID")
private Contact contact;
i am setting comment into contact pojo and finally i am saving contact.java
if i keep #OneToOne(optional= false, i am getting dataintegrityexception, constraintvoilationexception
if i changed #OneToOne(optional= true, then it is working.
i think that if optional is false, it is trying to insert contact, it find comment it is trying to insert comment, but it has reference to contact it has to set the foreign key without inserting contact it cannot keep foreign key ..
if optional true the contact can be inserted without comment and PK generated for contact and tat is set in the foreign key column of the contact.--- anyway this issue is solved.
one more thing i am loading contacts i need to lazy load the comments, it is no where working can some one help on this, i strictly need lazy loading of comment because of performance problm.
Thanks in advance.
We have this issue in hibernate 3. Hibernate 4 fixed this issue.
If you want to load only contacts not comments then try like this(Imply change it to manytoone relation).
#ManyToOne(fetch=FetchType.LAZY)
private Comment mComment;

Categories