Find rows in a unidirectional many-to-many mapping from the non-owning side in JPA/Nativequery/jpql - many-to-many

I have 2 entities that have a unidirectional many-to-many relationship, with a junction table between to keep track of the relationship. These entities are: Cat and Owner. Owner is the owning side of the relationship, so there is no information about owners in Cat. How do I write a nativequery to get all owners when providing a list of cats, the result of owners should be distinct. I tried solving this with Specifications, but OracleDB does not allow query.distrinct(true) on CLOB(Owner has a multiple CLOB fields). The class looks like this:
Public class Owner {
Long id;
#JoinTable(
name= "owner_cat",
JoinColumns = #JoinColumn(name = "Owner_id"),
inverseJoinColumns = #JoinColumn(name = "Cat_id")
)
Set<Cat> cats;
}
Public class Cat {
Long id;
}
Each row in the junction table have Owner id and Cat id.
I would appreciate if anyone can show me how I can solve this with either nativequery/jpql or jpa Specifications.
I tried getting all Owners by providing a list of Cats

Related

JPA: How to represent JoinTable and composite key of a JoinTable?

Let's say I have a webapp where users can follow other users.
In my database, I have a User table, and a Following table.
The Following table just has two values: a followingUserId and a followedUserId.
In Java, I have a User class. Most tutorials I see involve one object containing a set of objects it's related to. So many tutorials can describe how to have Users have a set of users following that user, and a set of users followed by a user. But that would require a lot of memory.
I'm thinking of an alternate structure where a User object has no info about following. Instead, there is a Following object that looks like this
#Entity
#Table(name = "Following")
public class Following {
RegisteredUser follower;
RegisteredUser followed;
}
and corresponds to the join table. When I want to get all the followers of a user, I can do a query for all Following objects with that user as the follower.
My issues are:
The Followers Table has a composite key of each of the two userids. How can I use annotations to represent that composite key? The #Id annotation denotes a single variable as the key
How can I do such a query?
If it's relevant, I am using MySQL as the db
If using JPA > 2.0, you can mark relationships as your ID:
#Entity
#Table(name = "Following")
#IdClass(FollowingId.class)
public class Following {
#Id
RegisteredUser follower;
#Id
RegisteredUser followed;
}
public class FollowingId implements Serializable {
private int follower;
private int followed;
}
the types within the followingId class must match the type of the RegisteredUser Id. There are plenty of more complex examples if you search on JPA derived ID.
For queries, it depends on what you are looking for. A collection of followers for a particular user could be obtained using JPQL:
"Select f.follower from Following f where f.followed = :user"

Retrieve data from three entities where two of them are in many-to-one relationships with the last one

I am using Spring Boot as REST backend app. Let's say that Person has Cats and Dogs and both Cat and Dog are in many-to-one relathionships with Person. By this, Cat and Dog have Person id as a foreign key. Since I'm using Spring JPA Repository and many-to-one relationship, it is straightforward to get list of cats with their persons and list of dogs with their persons. Those lists are transformed to json and I can access the person's data with frontend app. Here is my problem:
I want to return the list of all persons with all the cats and dogs for each person.
I guess that JPA Repository does not have a default query for my request, so I have to use custom queries. However, I do not know how to make it. I have tried the following one:
#Query("select p, c, d from Person p, Cat c, Dog d where c.person.id = :id and d.person.id = :id and person.id = :id")
List<Object[]> findAllPersonsWithCatsAndDogs(Integer id);
The idea was to run through for loop for each person and to use person's id to retrieve his cats and dogs. The result is the list of objects where each object has the same person, one of his cats, and one of his dogs. I do not like that, because then for all persons I have a list of lists of persons with their cats and dogs.
How to get one list of all persons with all the cats and dogs for each person.
Thanks
Here are the mappings to make it more clear:
#Entity
public class Person {
//there is no mappings because of unidirectional many to one
}
...
#Entity
public class Cat{
#ManyToOne
#JoinColumn(name = "person_id")
private Person person;
}
...
#Entity
public class Dog{
#ManyToOne
#JoinColumn(name = "person_id")
private Person person;
}
So, I have many to one unidirectional, which means that Person does not see cats and dogs.
You should add the reference between person, cat and dog.
#Entity
public class Person {
#OneToMany(cascade = CascadeType.PERSIST, mappedBy="person")
private List<Cat> catList;
#OneToMany(cascade = CascadeType.PERSIST, mappedBy="person")
private List<Dog> dogList;
}
Then if you want to get all the people with their cats and dogs you can do something like
#Query("SELECT p FROM Person P JOIN FETCH p.catList JOIN FETCH p.dogList")
List<Person> findAllPersonsWithCatsAndDogs()
(catList and dogList are just assumed to be the list in person due to not seeing your mapping).
This query will eager fetch your cats and dog list for every person. Then you can do
for (Person p : personRepo.findAllPersonsWithCatsAndDogs()) {
for (Cat c : p.getCastList()) {
}
for (Dog d : p.getDogList()) {
}
}
In general, the DTO Pattern is used to wrap up the necessary data. Therefore, you can use several object mapping frameworks such as ModelMapper, Modelstruct, Dozer.
If you create two repositories for the cat and dog Entity, you can do the following approach:
Method in CatRepository:
List<Cat> findByPersonId(int id);
Method in DogRepository:
List<Cat> findByPersonId(int id);
Querying the persons:
List<Person> persons = personRepository.findAll();
List<PersonDto> personsDto = new ArrayList<>();
foreach(Person p:persons) {
PersonDto dto = modelmapper.map(p, PersonDto.class);
p.setCats(catRepo.findByPersonId(p.getId()));
p.setCats(catRepo.findByPersonId(p.getId()));
personsDto.add(dto);
}

Using seperate OneToManys to create a ManyToMany Join table

I've got 2 models User and Exercise. Now any User can have any Exercise. It's a ManyToMany situation. I modeled it with #ManyToMany, but you can't have a duplicate entry in a ManyToMany. A User is likely to do multiple sets of one exercise so I duplicate entries are required. To get round this I created the join table separately called UserExerciseJoin. User and Exercise had ManyToOne relationships with the UserExerciseJoin model. Though this solved the multiple keys issue I now can't delete from the new table. I get an OptimisticLockException from some of the models associated to the Exercise.
My question is: Am I on the right path with the seperate table or is there something I can do to a standard #ManyToMany to make it accept duplicate entries?
If I understand it right in your model, then yes, it is probably not the case for #ManyToMany. It seems to me that you can be better off with a meaningful entity like UserExerciseOccurrence that reference both a User and an Exercise and means a concrete exercise session.
You can also benefit from this approach if you need to save more info about a particular exercise session (like duration, etc).
#Entity
class UserExerciseOccurrence {
#ManyToOne
User user;
#ManyToOne
Exercise exercise;
}
#Entity
class User {
#OneToMany(mappedBy="user", cascade=DELETE)
Set<UserExerciseOccurrence> exerciseOccurrences;
}
#Entity
class Exercise {
#OneToMany(mappedBy="exercise", cascade=DELETE)
Set<UserExerciseOccurrence> exerciseOccurrences;
}
You are on the right path. You should have #OneToMany relation from User class and from Excercise class to this new entity. And in UserExerciseJoin you should have #ManyToOne relations.
So this code should look like this:
#Entity
User {
#OneToMany(mappedBy="user")
private List<UserExercise> userExercises;
....
}
#Entity
Excercise {
#OneToMany(mappedBy="excercise")
private List<UserExercise> userExercises;
....
}
#Entity
UserExercise
{
#ManyToOne
private User user;
#ManyToOne
private Excercise excercise;
...
}
You had an error when deleting this new entity. You had in on some entity related to excercise. It seems that this is because of cascades. You probably set cascades on fields of UserExerciseJoin class. If it was CascadeType.DELETE or CascadeType.ALL cascade then it caused deletion of related entities. So you shouldn't set cascades in UserExercise class. Then deleting of such entity will not cause a problem.

Bidirectional mapping of entities in Hibernate and spring mvc

I am trying to map two entities User(userid,name,password,address) and Role(roleId,roleName).
I am trying to do bidirectional OneToMany mapping between User and Role.
My User entity:
#OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#JoinTable(
name="UserRole",
joinColumns = #JoinColumn(name="USER_ID"),
inverseJoinColumns = #JoinColumn(name="ROLE_ID")
)
public Set<Role> role;
My Role entity:
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(
name="UserRole",
joinColumns= #JoinColumn(name="ROLE_ID")
)
private User user;
Now the corresponding intermediate table created in the database has following attributes.
userrole-> attributes( user_userId,ROLE_ID,USER_ID )
Now when i add set of items to a user. ROLE_ID and USER_ID of userrole table gets populated but user_userId remain null. What is the purpose of user_useerId.Should i manully make it primary key in the table user_userId?
UPDATE:
i did the following editing in Role entity
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(
name="UserRole",
joinColumns = #JoinColumn(name = "ROLE_ID"),
inverseJoinColumns = #JoinColumn(name="USER_ID")
)
private User user;
And now when i checked the table in the database, the "userrole" table is perfectly ok and contains only (USER_ID,ROLE_ID)
I want to know why shouldn't i map two entities though this way?
In User entity you have declared #OneToMany with Role and also given details about the Join table using #JoinTable.
So if you need bidirectional between User and Role entities, then adding user property in Role entity with #ManyToOne declaration is sufficient, so no need of having #JoinTable once again.
Now coming to user_userId - in your Role entity you have declared the #JoinTable annotation and this annotation needs two column names for Join Table.
#JoinTable(
name="JoinTableName",
joinColumns = #JoinColumn(name="ID_NAME_FOR_THIS_ENTITY"),
inverseJoinColumns = #JoinColumn(name="ID_NAME_FOR_THE_MAPPING_ENTITY")
)
but you have provided one with name ROLE_ID and ignore the second name which is inverseJoinColumns that points to User entity.
#JoinTable(
name="UserRole",
joinColumns= #JoinColumn(name="ROLE_ID")
)
so hibernate should make a guess about the column name for inverseJoinColumns, so it creates a column with name as combination of the entity name in lower case (which is user in your case) then the identifier in that entity separated by underscore (which is userId I guess based on column name). So finally the column name becomes user_userId.
Update:
If you need bidirectional relationship then you need to declare your entities like this:
#OneToMany(mappedBy="user", cascade=CascadeType.ALL,fetch=FetchType.EAGER)
public Set<Role> role;
Here you are telling to hibernate that User entity has one-to-many relationship with Role entity and the relationship is bi-directional and the Role entity has a property called user.
Now in your Role entity you will give details about the JoinTable like this:
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(
name="UserRole",
joinColumns = #JoinColumn(name="ROLE_ID"),
inverseJoinColumns = #JoinColumn(name="USER_ID")
)
private User user;
The property name user in Role entity should match with the mappedBy attribute that you have declared in your User entity for the #OneToMany annotation. Adding the user field to Role entity makes the relationship bi-directional.
A (typical) bidirectional mapping has one side that is maintaining the relationship and the other side follows this relation ship.
This mean when you modifiy the maintaining side of the relation ship and save this change, then it gets stored in the database. While when you modifiy the following side, then nothing gets changed in the database. (it gets only updated when you load the entity).
A mapping would looks like:
#Entity
public class User {
...
#OneToMany(mappedBy="user") //mappedBy makes this side to the following side
public Set<Role> role;
...
}
#Entity
public class Role {
...
#ManyToOne
//#JoinTable... when you need it
private User user;
...
}
But this mapping is strange: because it mean that every User can have serveral roles, but each role belongs to exactly one user. -- If this is not intended, then switch ManyToOne and OneToMany or move over to ManyToMany

JavaEE 1:1 Unidirectional Relationship

I am confused about how the JPA handles a 1:1 unidirectional relationship when I auto generate my tables from my entity classes vs. how I would make those tables if I was creating the SQL tables myself.
My question concerns how the foreign keys are set up.
Assuming I have a Customer entity and an Address entity like this:
#Entity
public class Customer{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne(cascade = CascadeType.ALL)
private Address address;
//setters and getters
}
#Entity
public class Address{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String streetName;
//setters and getters
}
If I generate the sql tables from these two entity classes then the Customer table will have a foreign key column referencing the Address table.
Is there a way to have a OneToOne unidirectional relationship where the JPA sticks the foreign key on the target table(the address table) instead of on the Customer table as it does in my code example, or do I have to make it a bidirectional relationship and show more explicit ownership with the #Mapping annotation to achieve this kind of table structure?
To have the foreign key appear on the other table for a 1-1 unidirectional relationship you would need to switch the 'owning' side: place customer in Address and remove address from Customer. Then the foreign key for customer would be in the address table.
First of all, you have to map your address_id(or something like that) from the Customer entity. For example using below code, you are referencing the Address from the Customer.
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="ADDRESS_ID")
private Address address;
While deleting any entry from the address table, you must first delete the entry from the Customer table in order to avoid the foreign key violation error. Then you can delete from Address table. JPA doesn't handle this relationship while deleting any row from table.