#ManyToMany using 4 tables - java

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;
}

Related

How to remove child objects from a #ManyToMany relation with lots of children in JPA and Hibernate

Let's say I have two entities: Organization and User. Every user can be a member of many organizations and every organization can have many users.
#Entity
public class User {
#ManyToMany
Set<Organization> organizations;
//...
}
#Entity
public class Organization {
#ManyToMany(mappedBy="organizations")
Set<User> users;
//...
}
Now, I want to remove an organization (let's say it has 1000 members).
When the user has few organizations, this code is ok:
void removeOrgFromUser(Integer userId,Integer orgId){
User user = session.load(User.class, userId);
for (Organization org : user.organizations) {
if(org.getId().equals(orgId))
user.organizations.remove(org);
}
session.update(user);
}
But when organization count is 10,000, this solution does not have good performance.
How can I fix it?
If you have more than 50 or 100 child entities, you shouldn't map a collection.
Therefore, #OneToMany is misleading because, in reality, #OneToFew makes more sense. So, when many means 1000 or 10000, mapping such a collection becomes a real performance problem.
In this case, just break the #ManyToMany association so that you map the join table UserOrganization.
In this case, you just need the 2 #ManyToOne associations on the join table, and, you can just issue a bulk delete query like this:
delete from UserOrganization uo
where uo.organization = :organization
That's it!

Hibernate saving to db "Duplicate entry '1' for key 'UK_2n5xttsxwbyc6x67l0x8phfwn'"

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();

how to write POJO in hibernate having joins for more than 2 tables

I am new to hibernate, trying to get information from more than 2 tables using hql, if we pull that information we need to put in a POJO how to do mapping for each column to the information that we get as a result from the query?
Tried using #secondarytable() and #Table but it cannot allow more than 1 secondary table annotation.
So is it that we cannot have join for more than 2 tables in hibernate?
You would want to use the #OneToMany, #ManyToOne, and/or #ManyToMany annotations to map relationships between all of the entities in your project.
The oracle documentation for those has some pretty good examples, like this one:
Example 1: One-to-Many association using generics
In Customer class:
#OneToMany(cascade=ALL, mappedBy="customer")
public Set<Order> getOrders() { return orders; }
In Order class:
#ManyToOne
#JoinColumn(name="CUST_ID", nullable=false)
public Customer getCustomer() { return customer; }
In this example, both Customer and Order would be classes annotated with #Entity and #Table that correspond to CUSTOMER and ORDER tables in your database. Any other columns in those tables would also be mapped in the corresponding entity class. The #OneToMany, #ManyToOne and #ManyToMany are used to map foreign keys between your tables, and allow you to have direct references to parent/child rows/objects directly from your entity instances.
With these annotations, you can map as many foreign key references to other tables as you need.

Returning child entities from a parent entity with JPA join

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.

How can I access the underlying column after defining a #ManyToOne relationship on it in Spring?

I'm using Spring 3.2 with Roo 1.2.3 to build a database-backed Java application via Hibernate. I have several bidirectional OneToMany/ManyToOne relationships among the tables in my database. When I set up the ManyToOne side of the relationship using #JoinColumn (via "field reference" in Roo), a new field whose type is the related entity (the "one" in ManyToOne) is created. However, once this is done, there seems to be no way to access the underlying column value on which the ManyToOne relationship is based. This is a problem when the underlying join column contains data needed by the application (i.e. when the join column contains product stock numbers).
Is there any way to set up my entity class so that the column on which its ManyToOne relationship is based remains accessible without traversing the new join property? How can I define an accessor method for the value of this column?
I've been looking online for an answer to this question for several days, but to no avail. Thanks in advance for your help.
just map the column a second time with insertable=false and updateable=false
To make it more concrete. It's possible to do a HQL-SELCT and restrict a ManyToOne relationship, without any join in the resulting SQL:
Instead of using a join in
session.createQuery("FROM Person person WHERE person.adress.id = 42")
we use can use the adress_idcolumn
session.createQuery("FROM Person person WHERE person.adressId = 42")
This works, if you specify an additional adressId field, which is only used as mapping info for Hibernate:
#Entity
#Access(AccessType.FIELD)
public class Person{
#Id
String id;
#JoinColumn(name = "adress_id")
#ManyToOne(fetch = FetchType.LAZY)
#Nullable
public Adress adress;
#Column(name = "adress_id", insertable = false, updatable = false)
private String adressId;
}
#Entity
#Access(FIELD)
public class Adress{
#Id
String id;
}
The AccessType.FIELD is not needed (But we can leave getters/setters in example). The FetchType.LAZY and #Nullable are also optional, but make it clear when it makes sense to use it. We are able to load Person entities which have a specific Address (we know the address id). But we don't need a join because it's not needed for the WHERE-clause and not for the initial fetch (the address can be fetched lazy).

Categories