Spring Data JPA lazy fetching with collections - java

I have an User entity with Set cars (oneToMany). Also I have a method:
#Transactional(readOnly = true)
public Optional<User> getUserWithCars(Long id) {
return userRepository.findOneById(id).map(u -> {
u.getCars().size();
return u;
});
}
Lazy fetching works fine. But what if I have to fetch set of users with cars? I tried to use users.forEach(u -> u.getCars().size()); but received a popular no Session exception.
P.S. I need lazy fetch, not eager.

In repository:
#Query("SELECT u FROM User u LEFT JOIN FETCH u.cars")
public Set<User> getUserWithCars()

in spring data jpa you can write query method
Optional<User> findFirstWithCarsById(Long id);
for more information look to documentation

Related

Why do i only get the values when returning an object from a query?

public interface UserDao {
User getUserById(Long id);
void saveUser(User user);
List<UserDto> getAllUsers();
boolean isExistUserByEmail(String email);
boolean isExistUserByUserName(String userName);
// get users by id list
List<User> getUsersByIdIn(List<Long> idList);
// get all active users
List<UserDto> getAllActiveUsers();
User getUserByEmail(String email);
void saveAllUsers(List<User> userList);
List<Object[]> getAllInstructors();
}
This my dao impl method
#Override
public List<Object[]> getAllInstructors() {
return userRepository.getAllInstructors();
}
This is the query from my repository layer
#Query(value = "select distinct u.first_name, u.last_name, u.full_name, u.id from public.user u inner join public.users_group ug on u.id = ug.user_id and ug.group_list_id=1 WHERE status=1 ORDER BY u.id DESC", nativeQuery = true)
This is the method in my controller layer
#GetMapping("/instructors")
public List<Object[]> getAllIntsructors() {
return userService.getAllInstructors();
}
Result when I call the api on postman
The result I expect is:
first_name: "Iresha"
second_name: "Vishwakala"
But I don't get the key. I only get an array of objects showing me the values.
The way you are using your Repo is not a good practice. To be honest I have never seen it before.
This would be the correct way to do it:
public interface UserRepository extends JpaRepository<UserEntity, Long> // Here the first is the Entity which will be fetched by this repo, and the second is the type of ID that this Entity has.
{
// I am supposing that your entity is UserEntity and has an ID of type Long.
// Here come the queries which must return primitives or UserEntity (list, set etc.)
// An example query would be:
#Query(value = "....", nativeQuery = true) List<UserEntity> getUsersAsYouWant();
}
The JpaRepository makes use of Java Generics (hence the <>), that is why you must supply the class the types that it has to map the DB rows/data to. If you want to return specific types/Dtos/List you may use the same Repo but with different Query building (not native but JPQL). But that would be yet another question. If you want to check more, see my answer here.

Mapping #Query with multiple column selection to an Java object in a #Repository - is it possible out of the box?

Is it possible to map the results of a Hibernate #Query like this (in a #Repository interface extending #JpaRepository):
#Query("select u.id, u.email, u.status from user u")
public SimpleUserDTO getAllUsersSimpleData();
directly to a Java object like this:
public class SimpleUserDTO {
private Long id;
private String email;
private String status;
}
What I know, is that doing something like this:
Query query = session.createQuery("select u.id, u.email, u.status from user u");
the result can be extracted to a List<Object[]>:
List<Object[]> users = (List<Object[]>) query.list();
But is it possible to map it directly to a List<SimpleUserDTO> without writing additional method that will map the values to SimpleUserDTO?
You have two options.
Option 1: Constructor Expression:
#Query("select new <insert_package_here>.SimpleUserDTO(u.id, u.email, u.status) from user u")
public List<SimpleUserDTO> getAllUsersSimpleData();
Option 2: Use Interface projection
Turn your DTO into an interface and you can use it without #Query annotation
public List<SimpleUserDTO> getAllUsersSimpleData();
Please find out more about projections and Spring Data JPA in the documentation:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections

Spring Boot query from controller

I have User class like this :
#Data
#Entity
public class User {
#Id #GeneratedValue Long userID;
String eMail;
String passwordHash;
}
And I have data like this :
[{"userID":1,"passwordHash":"asdasd","email":"admin#admin.com"},
{"userID":2,"passwordHash":"12345","email":"admin1asdasd#admin.com"}]
I have two method in my controller class, one - to get single user :
#GetMapping("/user/{id}")
User one(#PathVariable Long id) {
return repository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
}
Other method to retrieve all user :
#GetMapping("/user")
List<User> all() {
return repository.findAll();
}
In my browser, going to this address - http://localhost:8080/user , I can see these data. And if I goto http://localhost:8080/user/id I can get a specific value.
Now my question is how can access data like http://localhost:8080/user/email/passwordHash? I am quite sure that it is not possible, because I haven't stored data in that way.
As my main target is to verify login, I have already written a #Query in my repository interface. Here it is :
public interface UserRepository extends JpaRepository<User, Long> {
#Query("select u from User u where u.eMail = ?1 and u.passwordHash = ?2")
List<User> listByLoginCredential(String emailAddress,String passwordHash);
}
Can Anyone tell me how can I do this,use this method of this interface?
I think you can can achieve what you want by adding the following method to the controller class:
#GetMapping("/user/{emailAddress}/{passwordHash}")
List<User> byMailAndPassword(#PathVariable String emailAddress, #PathVariable String passwordHash) {
return repository.listByLoginCredential(emailAddress, passwordHash)
}
On the other hand you say that your main goal is to verify login, so it looks like you are doing authentication. If you have time you should look into doing it with spring security https://spring.io/guides/gs/securing-web/#initial
Maybe this help https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.
Or you can also create procedure in Database and call stored procedure with Spring boot.
Login is related to security so ideally you should create a separate post method and use the repository method. Always make sure to follow the best practice.
Spring security is something you can utilize for better authentication and authorization.

Is Spring's QueryByExampleExecutor usable with Projections

Is there a way to return Iterable< IUser > where IUser is a projection of User entity.
Example<User> userExample = Example.of(user, userMatcher);
Iterable<User> foundUsers = userRepository.findAll(userExample, Sort.by("createdAt").descending());
Instead of sticking with spring data auto generated query and response type.
You can create your own HQL query by using #Query annotation so that you can except your custom return type.
Example :
#Query(" select user from User as user where user.userName like %:username% order by createdAt desc")
public List<User> findAllByUserName(String username);

Spring Boot and JPA Repository -- how to filter a GET by ID

I'm rewriting an application, this time using a RESTful interface from Spring. I'm presuming that server-side authorization is best. That is:
Supppose user 1 works this REST repository. He/she accesses mysite.com/heroes/1 and gets the (id = 1) hero from the hero table.
User 2 doesn't have rights to see the (id = 1) hero, but could craft a cURL statement to try anyway. I claim the server should prevent user 2 from accessing the (id = 1) hero.
I believe that the server can extract a JWT payload that gives me the user name or password (I put it in there). From that payload the server fetches the user's account and knows what heroes he/she is entitled to see.
I have already accomplished this goal through services and DAO classes. However, the Spring Boot and JPA tutorials I see promote using CrudRepository implementations to reduce coding. I'd like to know how to do my filtering using this technology.
Here is an example from the web:
#RepositoryRestResource(collectionResourceRel = "heroes", path = "heroes")
public interface HeroRepository extends CrudRepository<Hero, Long> {
}
When mysite.com/heroes/1 is accessed it automagically returns the data from hero (id = 1). I'd like to instruct it to let me choose which ID values to permit. That is, at runtime a query parameter is provided to it through code.
As a test I provided this code:
#RepositoryRestResource(collectionResourceRel = "heroes", path = "heroes")
public interface HeroRepository extends CrudRepository<Hero, Long> {
#Query ("from Hero h where id in (1, 3, 5)")
public Hero get();
}
However, it doesn't block mysite.com/heroes/2 from returning the (id = 2) hero.
How do I get to my desired goal?
Thanks, Jerome.
UPDATE 5/13, 5:50 PM
My request is being misunderstood, so I further explain my intent.
Users 1 and 2 are ordinary users, accessing their accounts.
Each user must be confined to his/her own account.
A user can't cheat by crafting requests for other peoples' data.
Thus the need for the server to extract a user ID, or such, from a JWT token and apply it in code to whatever causes the /heroes query to work.
My original example originated with this tutorial. In it the only Java classes are Hero and HeroRepository. There are no explicit classes for DAO, services or controllers. The included Spring libraries let all of the /heroes fetching occur without further coding.
Thanks again for all of your interest and help. Jerome.
You can create a custom #Query, that uses informations (here: id) of the logged in user. With this solution an user have only access to an entity with the same id as he has.
#Override
#Query("SELECT h FROM Hero h WHERE h.id=?1 AND h.id=?#{principal.id}")
public Hero findOne(Long id);
You need to enable SpEl for #Query (link) and create an custom UserDetailsService (link) with custom UserDetails, that contains the id of the user, so you can do principal.id.
In the same way you should secure the findAll() method.
I have created HeroRepository to resolve all the queries up to my understanding.
I'd like to instruct it to let me choose which ID values to permit.
You can achieve the same using.
List<Hero> findByIdIn(List<Long> ids);
Or, if you prefer Query
#Query("SELECT H FROM Hero H WHERE H.id IN :ids")
List<Hero> alternativeFindByIdIn(#Param("ids") List<Long> ids);
it doesn't block mysite.com/heroes/2 from returning the (id = 2) hero.
I cannot see your Controller/Service methods, so I am assuming that findOne() is being called. You can prevent it using..
// Disallow everybody to use findOne()
default Hero findOne(Long id) {
throw new RuntimeException("Forbidden !!");
}
OR, if you want more control over your method invocations, you can also use #PreAuthorize from spring-security.
// Authorization based method call
#PreAuthorize("hasRole('ADMIN')")
Optional<Hero> findById(Long id);
Summary
public interface HeroRepository extends CrudRepository<Hero, Long> {
// Disallow everybody to use findOne()
default Hero findOne(Long id) {
throw new RuntimeException("Forbidden !!");
}
// If u want to pass ids as a list
List<Hero> findByIdIn(List<Long> ids);
// Alternative to above one
#Query("SELECT H FROM Hero H WHERE H.id IN :ids")
List<Hero> alternativeFindByIdIn(#Param("ids") List<Long> ids);
// Authorization based method call
#PreAuthorize("hasRole('ADMIN')")
Optional<Hero> findById(Long id);
}
PS: Note that I am returning Optional<Hero> from the method. Optional.empty() will be returned if query produces no results. This will force us to check if the value is present before doing any operation, thereby avoiding NullPointerException.
use this code for Controller : -
#RestController
#RequestMapping("/cities")
public class CityController {
private static final Logger logger = LoggerFactory.getLogger(CityController.class);
#Autowired
private CityService cityService;
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public RestResponse find(#PathVariable("id") Long id) {
.
.
}
use below code for Repo :-
public interface CityRepo extends JpaRepository<FCity, Long> {
#Query("select e from FCity e where e.cityId = :id")
FCity findOne(#Param("id") Long id);
}
use below code for service :-
#Service
#Transactional
public class CityService {
#Autowired(required = true)
private CityRepo cityRepo;
public FCity findOne(Long id) {
return cityRepo.findOne(id);
}
}

Categories

Resources