JPA no session when calling child
-
21-12-2019 - |
Вопрос
I am using spring-data-jpa and JPA repositories
here is my source code
<beans:bean id="producerService" class="cz.services.RepositoryProducerService" />
<jpa:repositories base-package="cz.repository" />
<beans:bean id="myEmf"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<beans:property name="dataSource" ref="dataSource" />
<beans:property name="packagesToScan" value="cz.models" />
<beans:property name="jpaVendorAdapter">
<beans:bean
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</beans:property>
<beans:property name="jpaProperties">
<beans:props>
<beans:prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect
</beans:prop>
<beans:prop key="hibernate.show_sql">true</beans:prop>
</beans:props>
</beans:property>
</beans:bean>
<beans:bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<beans:property name="entityManagerFactory" ref="myEmf" />
</beans:bean>
<beans:bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<beans:property name="driverClassName" value="com.mysql.jdbc.Driver" />
<beans:property name="url"
value="jdbc:mysql://localhost:3306/mydb?zeroDateTimeBehavior=convertToNull&characterEncoding=UTF-8" />
<beans:property name="username" value="root" />
<!--<property name="password" value="test" /> -->
<beans:property name="password" value="test"></beans:property>
</beans:bean>
and here is my entity and repository classes:
package cz.models;
import java.io.Serializable;
import javax.persistence.*;
import java.util.List;
/**
* The persistent class for the users database table.
*
*/
@Entity
@Table(name="users")
@NamedQuery(name="User.findAll", query="SELECT u FROM User u")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private int enabled;
private String password;
private String username;
//bi-directional many-to-one association to Authority
@OneToMany(mappedBy="user")
private List<Authority> authorities;
//bi-directional many-to-one association to Room
@OneToMany(mappedBy="user")
private List<Room> rooms;
//bi-directional many-to-one association to UsersData
@OneToMany(mappedBy="user")
private List<UsersData> usersData;
public User() {
}
....
public List<Room> getRooms() {
return this.rooms;
}
and here are User Repository:
public void setRooms(List<Room> rooms) {
this.rooms = rooms;
}
public Room addRoom(Room room) {
getRooms().add(room);
room.setUser(this);
return room;
}
public Room removeRoom(Room room) {
getRooms().remove(room);
room.setUser(null);
return room;
}
public List<UsersData> getUsersData() {
return this.usersData;
}
public void setUsersData(List<UsersData> usersData) {
this.usersData = usersData;
}
public UsersData addUsersData(UsersData usersData) {
getUsersData().add(usersData);
usersData.setUser(this);
return usersData;
}
public UsersData removeUsersData(UsersData usersData) {
getUsersData().remove(usersData);
usersData.setUser(null);
return usersData;
}
}
and userRepository:
public interface UserRepository extends JpaRepository<User, Integer> {
@Transactional
@Query("select u from User u WHERE u.enabled = 1 ")
public List<User> findAllactiveUsers();
@Transactional
@Query("select u from User u WHERE u.username = :username ")
public User findByUsername(@Param("username")String username);
}
and my service for spring security:
@Service
public class MyUserDetailsService implements UserDetailsService {
@Resource
UserRepository repositoryUser;
@Resource
AuthorityRepository repositoryAuthority;
public UserDetails loadUserByUsername(String username) {
System.out.println("start");
cz.models.User userModel = null;
UserDetails userDetail = null;
try{
userModel = repositoryUser.findByUsername(username);
// User user = userModel;
System.out.println(userModel.getUsername());
List<Authority> authorities = repositoryAuthority.findAllByUser(userModel);
// repositoryUserData.findAll();
System.out.println(userModel.getAuthorities().size());
Collection<SimpleGrantedAuthority> collectionAuthorities = new ArrayList<SimpleGrantedAuthority>();
for (int i = 0; i < authorities.size(); i++) {
collectionAuthorities.add(new SimpleGrantedAuthority(authorities
.get(i).getAuthority()));
}
userDetail = new User(userModel.getUsername(),
userModel.getUsername(), collectionAuthorities);
}catch(Exception e){
e.printStackTrace();
}
return userDetail;
}
}
The problem is: when I want call - this peace of code:
userModel.getAuthorities()
which call child (fk in db). I have exception :
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
When I add second repository Authorities I do not have this no session problem. But I do not want create every time method in my repository.
List<Authority> authorities = repositoryAuthority.findAllByUser(userModel);
I have to use hibernate.LazyInitialization instead of eanger.(and some stable)
I saw a lot of post with this problem with JPA, but nothing work for me :(
Решение
Use a query that fetches the data you want so that it is there when you need it.
@Query("select u from User u left join fetch u.authorities WHERE u.username = :username ")
public User findByUsernameFetchAuthorities(@Param("username")String username);
Using findByUsernameFetchAuthorities when you want to access the authorities will cause them to be pre-fetched, avoiding the error and keeping them lazily fetched for every other query.