I have the Recipe.java object with #Entity:
...
#OneToMany(cascade = CascadeType.ALL)
private List<Category> category;
...
Then the Category.java object with #Entity:
...
#OneToOne(cascade = CascadeType.ALL)
private Name name;
...
Let's say that the db looks like this (recipe_category table):
Then the following code is executed (I just want to add a category to the recipe):
...
Recipe recipe = recipeRepository.findOne(recipeId);
Category ctg = categoryRepository.findOne(categoryId); // id=1
List<Category> categories = recipe.getCategory();
categories.add(ctg);
recipe.setCategory(categories);
recipeRepository.save(recipe);
...
On the recipeRepository.save(recipe) I am getting the following error:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'UK_2n5xttsxwbyc6x67l0x8phfwn'
So what would be the solution for this problem?
UPDATE:
the recipe table structure looks like this:
The category table structure looks like this:
So the problem seems to happen because when recipe.setCategory(categories); is triggered, it tries to save the ctg to the db, but it already exists. What I want is not to save it to db (because is's already there in the 'category' table) but to add a new row in the recipe_category table.
Maybe it has to do something with cascade?
Your relationship is not one-to-many. You want each recipe to have multiple categories. I guess you also want each category to be associated to multiple recipes. This is a many-to-many relationship. You need to use #ManyToMany annotation to configure your entities.
Also note that Hibernate relationships are always unidirectional. When you put #ManyToMany annotation in Recipe class you can access categories associated with a given recipe. In order to access the reverse relationship, to get recipes of a given category you also need to add an appropriate property with #ManyToMany annotation to Category class too.
I guess you used hbm2ddl or a similar method to auto create your tables and since tables are created for a one-to-many relationship you get an error. More specifically in a unidirectional one-to-many relationship the inverse foreign key column in join table (category_id in your recipe_category table) has a unique constraint defined on it. Thus with that table schema you cannot associate a category with more then one recipe.
Something like this should work:
// Recipe
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(name="category_map", )
private List<Category> categories = new ArrayList<> ();
and
// Category
#ManyToMany(cascade=CascadeType.ALL, mappedBy="categories")
private Set<Recipe> recipes;
Recipe recipe = recipeRepository.findOne(recipeId);
Category ctg = categoryRepository.findOne(categoryId); // id=1
List<Category> categories = recipe.getCategory();
categories.add(ctg);
recipe.setCategory(categories);
recipeRepository.save(recipe);
//categories.clear();
Related
How to join users and service through this two tables?
#JoinTable works perfect when you have relation through 1 table. But what to do in this case?
This is possible without creating separate entities for admins and admin_to_service?
You could try creating a view to function as the Join table. I don't know what your FK references are but something like:
create or replace view user_services_vw as
select
a.user_id,
ats.service_id
from
admins a
inner join
admin_to_services ats on ats.some_col. = a.some_col
You can then model as a simple #ManyToMany. This should work fine if the relationship is read only but you may have issues if the data needs to be updateable.
#Entity
public class User {
#ManyToMany
#JoinTable(name = "user_services_vw")
private Set<Service> services;
}
I Have 3 tables:
Invoice (id, userReadeableNum, ...)
Order (id, userReadeableNum, ...)
InvoiceOrderRel (id, invoceId, orderId, insertDate, ...)
I want to create a List property on Invoice Class with userReadableNum of Order class.
Something like this:
#Entity
#Table(name="INVOICE")
#Immutable
public class Invoice implements Serializable{
#Id
#Column(name="INV_ID")
private long invId;
//many attributes
/*I know that this annotation does not exists, i want to know if there's something similar*/
#ByQuery("select o.userReadableNum from Order o join o.invoiceOrderList iol where iol.order.invId = :invId")
private List<String> orderNums;
//etc...
I have never heard of something like that, but you could create a #ManyToMany relation to solve this problem. JPA and hibernate look at database tables as entities, so they expect PK and FKs.
With an #ManyToMany relationship, the orderNums property would need to be List<Order> as opposed to a list of strings. The InvoiceOrderRel would be your join table.
Here is a little information about #ManyToMany mappings.
I've got the following entity:
#Entity
#Table(name = "ONE")
#SecondaryTable(name = "VIEW_TWO", pkJoinColumns = #PrimaryKeyJoinColumn(name="ONE_ID"))
public class CpBracket {
#Id
private Long id;
#Column(name="progress", table="VIEW_TWO", updatable = false, insertable = false)
private int progress = 0;
(...)
}
As you see, this entity uses table ONE and (read only) view VIEW_TWO. When I'm persisting the entity, hibernate is performing insert into view:
insert into VIEW_TWO (ONE_ID) values (?)
It is ignoring the non-updatable and non-insertable column progress (that's good) and it is still trying to insert value of ONE_ID column. As far as I know, the annotation #PrimaryKeyJoinColumn marks selected column as insertable=false and updatable=false.
How can I prevent hibernate from inserting rows into secondary table (view)?
As far as I know, the annotation #PrimaryKeyJoinColumn marks selected
column as insertable=false and updatable=false.
I do not believe this can be the case: how then do we get records inserted into the #SecondaryTable when it is an actual table rather than a view?
As neither #SecondaryTable or #PrimarykeyJoinColumn have a means to prevent insert then it would appear that your original solution is not going to work and an alternative is required.
One option is to map VIEW_TWO as an #Entity and link to your class CPBracket as a #OneToOne relationship with cascade options set to none.
#Entity
#Table(name ="VIEW_TWO")
private CpBracketSummaryData(){
}
#Entity
#Table(name = "ONE")
public class CpBracket {
#OneToOne
#PrimaryKeyJoinColumn
private CPBracketSummaryData summaryData;
public int getSomeValue(){
return summaryData.getSomeValue();
}
}
The second option would be to use the non JPA compliant, Hibernate specific #Formula annotation.
#Entity
#Table(name = "ONE")
public class CpBracket {
#Formula("native sql query")
private int someValue;
}
Update October 2016
I have revisited this in both Hibernate 4.3.10.Final and 5.1.0.Final and it is possible to have the view as a #SecondaryTable without the insert: if you have the correct mappings.
Scenario 1
Load an entity for edit and do not touch any fields mapped to the secondary table. No update is issued to the secondary table
Scenario 2
Create and save a new entity and do not set any fields mapped to the secondary table. No insert is issued for the secondary table
Scenario 3
Create or update an entity including a field mapped to a secondary table and where this field is marked as insertable = false and updateable = false. An insert is made to the secondary table only for the ID field -the behaviour reported in the original question.
The issue with the mapping in the original question is the fact that the secondary table field is a primitive type and therefore when saving a new entity Hibernate does think a record has to be written to the secondary table with a value of zero.
#Column(name="progress", table="VIEW_TWO", updatable = false, insertable = false)
private int progress = 0;
The solution then is to replace primitives with the corresponding wrapper types and leave them as null. Then when saving a new record there is nothing to write to the secondary table and no insert will be made:
#Column(name="progress", table="VIEW_TWO")
private Integer progress;
I solved a similar problem with #SecondaryTable, which was a database view. So maybe it will help someone else.
The problem was on cascade delete to #SecondaryTable, when record from primary table was deleted.
As a solution, I implemented RULE on view for delete
CREATE RULE on_delete AS ON DELETE TO my_view DO INSTEAD(
select 1;
)
Similar solution can be used for INSERT and UPDATE operation on view.
i have the following two entities and one join table:
entity:
#Entity
#Table(name = "PERSON")
public class parent {
#OneToOne(mappedBy="person", cascade= CascadeType.ALL)
private Reader reader;
//more fields and getters and setters and of course #Id.....
}
and another entity:
#Entity
#Table(name="READER")
public class Reader{
#Id
#Column(name= "READER_ID")
#GeneratedValue(generator="gen")
#GenericGenerator(name="gen", strategy="foreign", parameters=#Parameter(name="property", value="Person"))
private long readerId;
#OneToOne
#PrimaryKeyJoinColumn
private Person person;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "READER_BOOKS", joinColumns={#JoinColumn(name="READER_ID")}, inverseJoinColumns={#JoinColumn(name="BOOK_ID")})
private List<Books> books;
//more fields and getters and setters
}
And i have a join table in the data base which is named "READER_BOOKS" which is consisted with two columns - readerId and bookId - both are a primary key as a pair, and each of the columns is a foreign key to the corresponding table.
A few details:
I have defined a "ManyToMany" relationship in Reader.java, meaning
that a reader can have many books ,and books can be reused in other
readers ( for example another reader can have the same books as other
readers). Also as you can see the reader id and the person id is the
same - so i defined a "OneToOne" relationship - which works fine.
In the books entity class i didn't add a reference or stated any
relationship because i understood that i don't need to.
The problem:
When i add a Person and set the reader field with several books, it works and the database is filled with the right values.(a person,reader and join tables are inserted with new rows)
The problem is when i delete the Person instance OR the Reader instance AND the books instance is not null(books are assigned to reader). hibernate is just stuck and not throwing any execption.
but when the books instance is null (didnt assign any books to the reader) the deletion is successfull.
I think that something might be wrong with the "ManyToMany" , because when the join table is empty the deletion is successful.
what is wrong in my implementation?
Edited: i found out sometimes hibernate gets stuck even if that list is empty, so i think something is also wrong with the "OneToOne" anottation, maybe i missed out something
there was no problem in my implementation.
i use PL/SQL developer and i didn't notice but it acquired a lock on one of the tables :|.
so when i tried to delete a record, hibernate got stuck and from some reason didn't throw any error.
Any way, the implementation is correct.
How can I return a list of entities that has a relationship to a parent in JPA?
I have a User entity that has a #OneToMany mapping on a property named pets. The child entities are of type Pet. It is only a uni-directional relationship.
How can I write a join in JPA that returns all pets given a user?
So you have a couple of options.
You can use the following annotations:
#ManyToOne
#JoinColumn
This is how you would use it.
public class User
{
// your standard fields / columns in database
#OneToMany (Fetch can be of eager/ lazy)
#JoinColumn (name="column to join on", referencedColumnName="column to join on in parent class")
private List<Pet> pets;
}
public Class Pet
{
//data fields
}
What essentially happens is the list of pets is populated when you are querying for the user object.
Using JPA to Query the DB.
So i am guessing that Your user would have some sort of id and the pet table would have some sort of Id to the user that are linked.
So we would do the following
Select * from user where user_id = ?;
this will essentially give you the user object
Select * from pet where owner_user_id = ?
this will essentially give you all the pets that belong to that user.
Then you can populate your object yourself.
I am not 100% sure of how your table looks like, but I was hoping to give it a stab from just what I would do point of view.