Spring Boot 和 fetchType=Lazy
Posted
技术标签:
【中文标题】Spring Boot 和 fetchType=Lazy【英文标题】:Spring Boot and fetchType=Lazy 【发布时间】:2017-05-31 06:37:15 【问题描述】:我的 Spring Boot 应用程序中存在延迟初始化问题。我有一个带有惰性字段Role
的实体,我在我的Spring Security (UserDetailsService
) 方法中有LazyInitializationException
,但在控制器中没关系。
你能向我解释一下 Spring Boot 如何与 fetch = FetchType.LAZY
一起工作吗?为什么它不能在 Spring Security UserDetailsService
和控制器方法中工作?
我没有找到任何关于此的指南。谢谢!
春季启动:
@SpringBootApplication
public class App
public static void main(String[] args) throws Exception
SpringApplication.run(App.class, args);
一个实体:
@Entity
@Table(name = "users")
@Getter
@Setter
@NoArgsConstructor
public class Users
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "Users_Role", joinColumns = @JoinColumn(name = "User_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
private Set<Role> roles = new HashSet<Role>();
我的服务:
@Transactional(readOnly = true)//does not matter
public Users getUserByLogin(String login)
return usersRepository.findOneByLogin(login);
@Transactional(readOnly = true)
public Users getUserByLoginWithRoles(String login)
Users oneByLogin = usersRepository.findOneByLogin(login);
logger.debug("User was initialize with Roles: " + oneByLogin.getRoles().size()); // force initialize of roles and it works!
return oneByLogin;
@Transactional(readOnly = true)//does not matter
public Users testGetUser()
Users oneByLogin = usersRepository.getOne(1L);
return oneByLogin;
@Transactional(readOnly = true)//does not matter
public Users testFindUser()
Users oneByLogin = usersRepository.findOne(1L);
return oneByLogin;
我有 Spring Security UserDetailsService:
@Service
public class UserDetailsServiceImpl implements UserDetailsService
@Autowired
private Services services;
@Override
public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException
Users user;
Users userFetchedViaGet = services.testGetUser();
Users userFetchedViaCustomMethod = services.getUserByLogin(login);
Users userFetchedViaFind = services.testFindUser();
Users userFetchedWithRoles = services.getUserByLoginWithRoles(login);
try
userFetchedViaGet.getRoles().add(new Role("test"));
catch (Exception e)
e.printStackTrace();//LazyInitializationException: failed to lazily initialize a collection of role: , could not initialize proxy - no Session
try
userFetchedViaCustomMethod.getRoles().add(new Role("test"));
catch (Exception e)
e.printStackTrace();//LazyInitializationException: failed to lazily initialize a collection of role: , could not initialize proxy - no Session
try
userFetchedViaFind.getRoles().add(new Role("test")); //LazyInitializationException: failed to lazily initialize a collection of role: , could not initialize proxy - no Session
catch (Exception e)
e.printStackTrace();
//some code
和我的控制器(所有方法都有效!但是必须发生异常,因为没有会话和延迟获取类型):
@RequestMapping(value = "/test", method = RequestMethod.GET)
public String test()
Users userFetchedViaGet = services.testGetUser();
Users userFetchedViaCustomMethod = services.getUserByLogin("ADMIN");
Users userFetchedViaFind = services.testFindUser();
Users userFetchedWithRoles = services.getUserByLoginWithRoles("ADMIN");
try
userFetchedViaGet.getRoles().add(new Role("test"));
catch (Exception e)
e.printStackTrace();
try
userFetchedViaCustomMethod.getRoles().add(new Role("test"));
catch (Exception e)
e.printStackTrace();
try
userFetchedViaFind.getRoles().add(new Role("test"));
catch (Exception e)
e.printStackTrace();
//some code
【问题讨论】:
你试过这个***.com/questions/11746499/… 您好!那篇文章与我的问题没有任何共同之处。 你能解决这个问题吗?我其实也有同样的问题 【参考方案1】:您需要一个交易来包装整个事物 - 在您的外观上 - test() 方法才能使其工作,但这并不是一个真正的好习惯。
要走的路是让您的服务将 api 方法包装在事务中,并返回一个完全加载的对象 - 包含所有子节点。这个对象可以是一个从 Users 类构造的简单 bean, 从对象构建的哈希图,甚至是你想从 rest 调用中返回的字符串。
【讨论】:
您好!默认情况下,Spring Data Jpa 在事务 readOnly=true 中包装诸如 testGetUser() 之类的方法。我在方法 getUserByLoginWithRoles 中做到了。为什么像“testGetUser”这样的方法在安全类中不起作用而在控制器中不起作用?为什么在控制器中我没有收到 Spring Boot 的延迟异常?为什么 Spring Boot 初始化了我的惰性集合? Spring Boot 是怎么做到的? 嗨。问题是事务在使用 spring data 方法后立即结束,因为您在 User 和 Role 实体之间使用惰性获取,所以 spring data 方法只会获取没有角色的用户。如果包装在更大的事务上下文中,则应该可以遍历用户角色图并从数据库中获取所有角色。在这种情况下,如果可能的话,你显然可以将惰性获取更改为渴望,或者如果你不想让这个渴望,你可以编写一个简单的 hql 查询来强制获取这个连接。以上是关于Spring Boot 和 fetchType=Lazy的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot 2. Jackson json 序列化或 Spring Data JPA FetchType.Lazy
如何在 Spring Boot 中使用 JsonIgnore 来停止无限循环? [复制]
如何在Jpa中使用所选实体创建行,RestController Spring Boot