How to get entities that have a collection and one element of this collection in another collection - many-to-many

Now I am trying to implement a function like filter. There are two models User and Role. I used unidirection many-to-many relationship. Below is my code.
public class User {
...
#ManyToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
#JoinTable(name = "user_role", joinColumns = {#JoinColumn(name="user_id") },
inverseJoinColumns = { #JoinColumn(name = "role_id") })
private Set<Role> roles = new HashSet<Role>();
...
}
public class Role {
private String role;
...
}
Now I want to get users who have many roles but exactly just one of the user's roles in the role collection that the user is picked out. I want to use HQL to query but I don't how to so I write a sql "select * from user as u left join user_role as ur on u.id = ur.user_id
where ur.role_id in (1,2)". But the result couldn't be converted to be a list of user. And I still need to use HQL, so does anyone can give some help. Thanks.

The translation of your SQL query is
select u from User u join u.roles role
where role.id in (1,2)
See the documentation to learn about HQL and joins.

Related

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

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

How to execute native query with spring-data-jpa on entity with persistence relations

I'm trying to use spring-data-jpa against a mysql 5.7 database to find entities using a regular expression. My native query on a jpaRepository method is producing errors.
I'm replacing an old custom-built c++ server used for licensing with Spring. I cannot change the database structure nor the api.
I'm using spring-boot-starter-data-jpa:2.1.4.RELEASE, which users hibernate-core:5.3.9.Final and spring-data-jpa:2.1.6:RELEASE.
My api implements the following endpoint: licenses/search/{fieldName}:{regex}/{limit}/{offset}
for example: licenses/search/edition.name:"^Edition X$"/1/0
My DBLicense entity has a #OneToMany relationship with DBEdition.
At first I tried writing a query method in a LicenseRepository, as described here:
#Repository
public interface LicenseRepository extends JpaRepository<DBLicense, Long> {
...
List<DBLicense> findByEditions_NameRegex(String searchStr, Pageable pageRequest);
...
}
But I kept receiving the following error: unsupported keyword regex (1): [matchesregex, matches, regex]. The documentation indicates that regex might not be supported, and to check store-specific docs, which I could not find. Other answers led me to try the #Query annotation.
Because JPQL does not support regex, I opted to use the native query:
#Repository
public interface LicenseRepository extends JpaRepository<DBLicense, Long> {
...
#Query(value = "select l.* from licenses as l join licenseeditions as le on l.LicenseID=le.LicenseID join editions as e on le.EditionID=e.EditionID where e.Name regexp :searchStr limit :offset, :limit", nativeQuery = true)
List<DBLicense> findByEditions_NameRegex(#Param("searchStr") String searchStr, #Param("offset") Integer offset, #Param("limit") Integer limit);
...
}
and I receive the following error:
2019-07-18 11:46:50.145 WARN 24524 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: S0022
2019-07-18 11:46:50.146 ERROR 24524 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : Column 'ParentID' not found.
My DBLicense class:
#Entity
#Table(name = "licenses")
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class DBLicense {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "LicenseID")
...
#ManyToOne
#JoinTable(name = "licensekinships", joinColumns = #JoinColumn(name = "ChildID", referencedColumnName = "LicenseID"), inverseJoinColumns = #JoinColumn(name = "ParentID", referencedColumnName = "LicenseID"))
private DBLicense parentLicense;
...
#OneToMany
#JoinTable(name = "licenseeditions", joinColumns = #JoinColumn(name = "LicenseID", referencedColumnName = "LicenseID"), inverseJoinColumns = #JoinColumn(name = "EditionID", referencedColumnName = "EditionID"))
#Setter(AccessLevel.NONE)
#Builder.Default
private List<DBEdition> editions = new ArrayList<DBEdition>();
}
The query executes successfully in mysql (I checked the log), and an error is thrown sometime after it returns inside Spring.
Notice that none of the tables referenced in my #Query (i.e. licenses,licenseeditions,editions) contain a 'ParentID' column. 'ParentID' is found on licensekinships, which is the relationship table of the many to one relationship between licenses and licenses.
Does my native query need to account for all the other relationship annotations on DBLicense? That's problematic because there are a LOT (the built-in LicenseRepository findById method executes no less than 59 queries!).
If you're using the hibernate/javax.persistence relationship annotations on your entities (i.e. #OneToOne, #OneToMany, #ManyToOne, #ManyToMany), and you attempt to use native queries, then you may have the same issue as presented in the question post.
This will happen especially if you have a complex schema wherein one entity shares a relationship with another, which in turn has further relationships, and you're trying to return one of those entities from a native query. To fix this error, you will need to provide enough information in your native query for spring-data-jpa to resolve the relationships which are present in your entities.
For example, consider the following class objects:
#Entity
#Table(name = "entity_a")
public class EntityA {
#Column
private int entityA_field
...
#ManyToOne
private EntityB entityB
}
and
#Entity
#Table(name = "entity_b")
public class EntityB {
#Column
private int entityB_field
...
#ManyToOne
private EntityC entityC
}
the JpaRepository built-in findById method for a EntityA id might execute multiple database queries. Using a sql database, for example:
select a.*, b.* from entity_a as a left outer join entity_b as b on a.id = b.id;
select b.*, c.* from entity_b as b left outer join entity_c as c on b.id = c.id;
you will need to mimic that first query's joins and columns. Luckily, you can see the pseudo-sql generated by spring-data-jpa by turning on logging:
find and open your application.properties (or application.yaml) file. Usually is located in "src/main/resources".
Add the following line if not present: spring.jpa.show-sql=true, then save.
Make a repository for the entity returned by your native query. For example, if your native query returns an EntityA, then your repository might look like:
#Repository
public interface MyRepository extends JpaRepository<EntityA, Long> {}
Call your repository findById method (from a controller, or a test) and check the console output. A number of queries will be logged to the console. Your native query needs to provide the same columns and implement the same joins as the first of these queries.

Spring boot, execute custom query

Im newbie to web development,and I did some examples like get data from mysql db and show them in a jsp pages.(use CRUDRepository )
but in that way we can only show only one table data.
what should we do if we want to show combine two table data.
I found these while um searching,simply I m asking how we put a more complicated sql query to this.
public interface UserRepository extends JpaRepository<User, Long> {
#Query("select u from User u where u.lastname like ?1%")
List<User> findByAndSort(String lastname, Sort sort);
#Query("select u.id, LENGTH(u.firstname) as fn_len from User u where u.lastname like ?1%")
List<Object[]> findByAsArrayAndSort(String lastname, Sort sort);
}
if we can put that complicated query (like three tables or more) here,
should we create a new entity class according to query coloumns ??
then again is that work because actually there isn't any table like that.
To get more complex data from DB you can use projections, for example:
public interface UserProjection {
Long getId();
Long getFirstNameLen();
}
#Query("select u.id as id, LENGTH(u.firstName) as firstNameLen from User u where u.lastname like ?1%")
List<UserProjection> getProjections(String lastName, Sort sort);
Note that you should use aliases in the query that must match with getters in the projection (... as firstNameLen -> getFirstNameLen())
The same way you can get data from several (joined) entities.
If you have an entity with some associations, for example:
#Entity
public class User {
//...
#OneToMany
private List<Role> roles;
}
then you can use repository method to get the users and their roles data even without any projection, just for the main entity (User). Then Spring does the rest of the work itself:
#EntityGraph(attributePaths = "roles")
List<User> findByFirstNameContainingIgnoreCase(String firstName);
or the same with query:
#Query("select distinct u from User u left join fetch u.roles where upper(p.firstName) like concat('%', upper(?1), '%')")
List<User> findWithQuery(String firstName);
In this case all users will have their lists of roles are filled with data from the roles table.
(Note to use distinct in the query to prevent the result from duplicated records, more info see here.)
Useful resources:
Spring Data JPA - Reference Documentation
Query Creation
Repository query keywords
Projections
SpEL support in Spring Data JPA #Query definitions
Hibernate ORM User Guide
Associations
HQL and JPQL
JPQL Language Reference

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

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