Spring boot + jpa lazy fetch

I'm new here, hope you can help me guys.

I have a problem with lazy fetch type.

I do not like to receive all relationships only if I need it. When I use the CommandLineRunner.run method, it is fine, it retrieves the Ribbons, but if I call this method from RestController , it always retrieves it impatiently, but I do not want this.

I tried:

  • with a DTO and without a DTO object.
  • Update all dependencies to the latest version.
  • Change @RestController annotation to @Controller
  • @Query annotation with LEFT JOIN FETCH by custom method in repository
  • @Lazy (value = true)
  • @Basic (fetch = FetchType.LAZY)
  • @LazyCollection (value = LazyCollectionOption.TRUE)
  • @LazyToOne (value = LazyToOneOption.NO_PROXY)
  • @ElementCollection (fetch = FetchType.LAZY)
  • And of course, I tried putting fetch = FetchType.LAZY in both @ManyToMany, @ManyToOne ... annotation and cascaded too.
  • Using the private administrator EntityManager @PersistenceContext with createQuery ();

and finally i am using spring security using CustomUserDetailsService. When I log in, it returns with a User object. If the @Transactional annotation is in the ServiceImpl class, it retrieves them impatiently, but if I delete this annotation, it will receive the Ribbon, but this only works when I log in.

Do you have any ideas?

Repository:

 import hu.pte.clms.model.domain.User; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.stereotype.Repository; @Repository public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor{ } 

ServiceImpl:

 import hu.pte.clms.model.domain.User; import hu.pte.clms.repository.UserRepository; import hu.pte.clms.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Service @Transactional public class UserServiceImpl implements UserService{ @Autowired private UserRepository userRepository; @Override public List<User> listAll(){ return userRepository.findAll(); } /* And another methods with this scheme */ 

Controller:

 import hu.pte.clms.model.domain.User; import hu.pte.clms.model.dto.UserDTO; import hu.pte.clms.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.stream.Collectors; @RestController @RequestMapping("/api") public class UserController{ @Autowired private UserService userService; @RequestMapping(value = "/user/all", method = RequestMethod.GET) public ResponseEntity<List<UserDTO>> listAll(){ return new ResponseEntity<>(userService.listAll().stream().map(user -> new UserDTO(user.getId(), user.getFirstName(), user.getLastName(), user.getCity(), user.getCountry(), user.getBio(), user.getPictureUrl())).collect(Collectors.toList()), HttpStatus.OK); } @RequestMapping(value = "/auth/user") public LoginResult get(){ Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if(!auth.getName().equals("anonymousUser")){ User user = userService.findByUsername(auth.getName()); return new LoginResult(auth.getName(), auth.getAuthorities(), user); } return null; } } 

LoginResult:

 import hu.pte.clms.model.domain.User; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.List; public class LoginResult implements UserDetails{ private String password; private String name; private User user; private Collection<? extends GrantedAuthority> authorities; public LoginResult(String name, Collection<? extends GrantedAuthority> authorities, User user){ this.name = name; this.authorities = authorities; this.user = user; } public LoginResult(String username, String s, boolean b, boolean userNonExpired, boolean credentialsNonExpired, boolean userNonLocked, Collection<? extends GrantedAuthority> authorities){} public LoginResult(String username, String password, List<GrantedAuthority> grantedAuthorities){ this.name = username; this.password = password; this.authorities = grantedAuthorities; } 

User:

 import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import hu.pte.clms.model.domain.relationship.UserSkill; import javax.persistence.*; import java.io.Serializable; import java.util.ArrayList; import java.util.List; @Entity @Table(name = "USER") @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(value = JsonInclude.Include.NON_NULL) public class User implements Serializable{ private static final long serialVersionUID = 1L; @Id @GeneratedValue private Long id; @Column(name = "USERNAME") private String username; @Column(name = "PASSWORD") private String password; @Column(name = "FIRST_NAME") private String firstName; @Column(name = "LAST_NAME") private String lastName; @Column(name = "AGE") private Short age; @Column(name = "SEX") private String sex; @Column(name = "PHONE") private String phone; @Column(name = "SKYPE") private String skype; @Column(name = "PRIMARY_EMAIL") private String primaryEmail; @Column(name = "SECONDARY_EMAIL") private String secondaryEmail; @Column(name = "CITY") private String city; @Column(name = "COUNTRY") private String country; @Column(name = "BIO") private String bio; @Column(name = "PICTURE_URL") private String pictureUrl; @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "CONFIG_ID") private Config config; @JsonIgnore @ManyToMany @JoinTable(name = "REL_USER_ROLE", joinColumns = {@JoinColumn(name = "USER_ID")}, inverseJoinColumns = {@JoinColumn(name = "ROLE_ID")}) private List<Role> roles = new ArrayList<>(); @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "REL_USER_SECURITY_ROLE", joinColumns = {@JoinColumn(name = "USER_ID")}, inverseJoinColumns = {@JoinColumn(name = "SECURITY_ROLE_ID")}) private List<SecurityRole> securityRoles = new ArrayList<>(); @ManyToMany(mappedBy = "user") private List<UserSkill> skills = new ArrayList<>(); @JsonIgnore @ManyToMany @JoinTable(name = "REL_USER_PROJECT", joinColumns = {@JoinColumn(name = "USER_ID")}, inverseJoinColumns = {@JoinColumn(name = "PROJECT_ID")}) private List<Project> projects = new ArrayList<>(); @JsonIgnore @OneToMany(mappedBy = "reviewed", cascade = CascadeType.ALL) private List<Review> reviews = new ArrayList<>(); /*Getters & setters*/ 

UserDTO:

 import com.fasterxml.jackson.annotation.JsonInclude; import hu.pte.clms.model.domain.*; import hu.pte.clms.model.domain.relationship.UserSkill; import java.util.ArrayList; import java.util.List; @JsonInclude(JsonInclude.Include.NON_EMPTY) public class UserDTO{ private Long id; private String username; private String password; private String firstName; private String lastName; private Short age; private String sex; private String phone; private String skype; private String primaryEmail; private String secondaryEmail; private String city; private String country; private String bio; private String pictureUrl; private Config config; private List<Role> roles = new ArrayList<>(); private List<SecurityRole> securityRoles = new ArrayList<>(); private List<UserSkill> skills = new ArrayList<>(); private List<Project> projects = new ArrayList<>(); private List<Review> reviews = new ArrayList<>(); public UserDTO(){ } public UserDTO(User user){ this.id = user.getId(); this.username = user.getUsername(); this.password = user.getPassword(); this.firstName = user.getFirstName(); this.lastName = user.getLastName(); this.age = user.getAge(); this.sex = user.getSex(); this.phone = user.getPhone(); this.skype = user.getSkype(); this.primaryEmail = user.getPrimaryEmail(); this.secondaryEmail = user.getSecondaryEmail(); this.city = user.getCity(); this.country = user.getCountry(); this.bio = user.getBio(); this.pictureUrl = user.getPictureUrl(); this.config = user.getConfig(); this.roles = user.getRoles(); this.securityRoles = user.getSecurityRoles(); this.skills = user.getSkills(); this.projects = user.getProjects(); this.reviews = user.getReviews(); } public UserDTO(Long id, String firstName, String lastName, String city, String country, String bio, String pictureUrl){ this.id = id; this.firstName = firstName; this.lastName = lastName; this.city = city; this.country = country; this.bio = bio; this.pictureUrl = pictureUrl; } /*Getters & setters*/ } 

Application.java:

 import hu.pte.clms.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application implements CommandLineRunner{ @Autowired private UserService userService; public static void main(String[] args) { SpringApplication app = new SpringApplication(Application.class); app.setShowBanner(false); app.setRegisterShutdownHook(true); } @Override public void run(String... strings) throws Exception{ userService.listAll(); } } 

pom.xml:

  ... <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.2.2.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.4.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring.boot.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>${spring.boot.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>${spring.boot.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>${spring.boot.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.1.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>4.1.4.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.34</version> </dependency> </dependencies> 

Application.yml:

 spring.datasource: url: jdbc:mysql://localhost:3306/clms?autoReconnect=true username: clms password: clms testOnBorrow: true validationQuery: SELECT 1 driverClassName: com.mysql.jdbc.Driver 
+7
java spring spring-mvc hibernate jpa
source share
2 answers

** Sorry, I cannot write a comment, so I am writing here.

The only thing I could see here is the @Transactional annotation in the service class, and the default distribution is required.

One more thing in Jackson @JsonIgnoreProperties and @JsonIgnore do not work. Better put the properties to ignore in @JsonIgnoreProperties (value = {"projects", "reviews"})

Hope this helps.

0
source share

You must perform the mapping in the database, not in your code. i.e.

 SELECT NEW package.UserDTO(user.id, user.firstName, user.lastName, user.city, user.country, user.bio, user.pictureUrl) FROM User user 

This guaranteed that only fields from the constructor would be used when you use such a query in JPQL format.

0
source share

All Articles