2016-02-18 21 views
7

Sono nuovo qui, spero che tu possa aiutarmi ragazzi.Spring boot + jpa lazy fetch

Ho un problema con il tipo di recupero lazy.

Non mi piace recuperare tutte le relazioni, solo se ne ho bisogno. Quando uso il metodo CommandLineRunner.run, va bene, sta recuperando Lazily, ma se chiamo questo metodo da RestController, sto sempre recuperando con impazienza ma non lo voglio.

Ho provato:

  • con DTO e senza oggetto DTO.
  • aggiorna tutte le dipendenze alla versione più recente.
  • Change @RestController annotazione @Controller
  • @query annotazione con LEFT JOIN FETCH su un metodo personalizzato nel Repository
  • @Lazy (valore = true)
  • @Basic (prendere = FetchType.LAZY)
  • @LazyCollection (value = LazyCollectionOption.TRUE)
  • @LazyToOne (value = LazyToOneOption.NO_PROXY)
  • @ElementCollection (prendere = FetchType.LAZY)
  • E certo, I Tri per mettere fetch = FetchType.LAZY e nelle annotazioni @ManyToMany, @ManyToOne ... e con le cose a cascata.
  • Utilizzo del gestore di EntityManager privato @PersistenceContext con createQuery();

e infine, sto usando la sicurezza di primavera con CustomUserDetailsService. Quando effettuo l'accesso, restituisce l'oggetto Utente. Se l'annotazione @Transactional si trova nella classe ServiceImpl, sta recuperando con impazienza, ma se rimuovo quell'annotazione, è necessario scaricare Lazily, ma questo funziona solo per l'accesso.

Avete qualche idea?

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

Utente:

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 
+1

Potresti provare a restringere il problema, per non pubblicare l'intero codice dell'applicazione? – aribeiro

+0

L'ho condiviso perché forse mi sono sbagliato in qualcosa e qualcuno ha notato. Ho scritto il mio problema all'inizio, non è necessario passare attraverso il codice. – zse014

+0

Come ha detto @aribeiro, meglio provare a fornire un esempio semplificato di cosa sta succedendo, in modo che le persone possano testarlo. Non vedo tutto il codice utile per risolvere il caso. Dai un'occhiata a [questo] (http://sscce.org/). –

risposta

0

** Mi dispiace non posso davvero scrivere un commento così sto scrivendo qui.

L'unica cosa che ho potuto vedere qui è l'annotazione @Transactional nella classe di servizio e la propagazione predefinita è richiesta.

Un'altra cosa, in Jackson @JsonIgnoreProperties e @JsonIgnore non funziona. Meglio mettere le proprietà da ignorare in @JsonIgnoreProperties (valore = {"progetti", "recensioni"})

Spero che questo possa essere d'aiuto.

+0

Grazie per il feedback, l'ho modificato. Ma sfortunatamente non ha cambiato nulla. :( – zse014

0

È necessario eseguire la mappatura in DB, non nel codice. vale a dire

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

E 'garantito che solo i campi da costruttore saranno recuperati quando si sta utilizzando Query JPQL come questo.