我基于 springboot 创建了一个用户管理服务。
用户将具有附件的列表,因此用户与附件的关系为一对多。
我在这里忽略了插入逻辑,因为我的问题是关于 lazyload 的问题以及 entitymanager 的打开和关闭时间。下面是与实体,控制器,服务,Dao,存储库相关的代码。
实体
@Entity
@Table(name="User")
public class UserInfoEntity {
private long id;
private String mail;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "userInfoEntity", cascade = CascadeType.ALL)
private List<UserAttachmentEntity> attachmentList = new ArrayList<>();
}
@Entity
@Table(name = "ATTACHMENT")
public class UserAttachmentEntity {
private long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="userRequestId")
private UserInfoEntity userInfoEntity;
}
服务
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
@Transactional
public void save(UserInfoEntity userInfoEntity) throws RestException {
userDao.save(userInfoEntity);
}
@Override
// I did set @Transactional here
public UserInfoEntity findByMail(String mail) {
UserInfoEntity userInfoEntity = userDao.findByMail(mail);
return userInfoEntity;
}
}
DAO
@Service
public class UserDaoImpl implements UserDao {
@PersistenceContext
private EntityManager entityManager;
@Autowired
private UserInfoEntityRepository userInfoRepository;
@Override
public UserInfoEntity findByMail(String mail) {
return userInfoRepository.findFirstByMailOrderByIdDesc(mail);
}
}
存储库
@Repository
public interface UserInfoEntityRepository extends JpaRepository<UserInfoEntity, Integer> {
UserInfoEntity findFirstByMailOrderByIdDesc(String mail);
}
控制器
@Controller
@RequestMapping(value = "/user")
public class UserController {
@RequestMapping(value = "load", method = RequestMethod.POST)
@ResponseBody
public UserInfoEntity load(UserInfoEntity userInfoEntityInput, HttpServletRequest request) throws Exception {
UserInfoEntity userInfoEntity = userService.findByMail(userInfoEntityInput.getMail());
System.out.println(userInfoEntity.getAttachmentList());
return userInfoEntity;
}
}
在控制器中测试 load 方法后,我发现甚至设置了
@OneToMany(fetch = FetchType.LAZY,...) UserInfoEntity 。
我仍然可以在控制器中调用 userInfoEntity.getAttachmentList()。 (我可以看到从附件中选择* 查询已打印在那里)
帖子lazyinitializationexception-in-spring-data-jpa的回答是,您需要在事务内时获取惰性数据。
但是我没有在服务中的 findByMail 方法中设置 @Transaction 。
我记得几天前我也遇到了这个例外。但是现在我可以在控制器中成功加载惰性数据了。
我主要有以下问题。
先谢谢了。我在公司中写了这个问题,不允许在公司中将任何代码推送到github。如果可以的话,我认为这将为您带来更多便利,因为带有h2数据库的spring boot项目在本地很容易设置。
答案 0 :(得分:5)
如果您使用Appendix A. Common application properties,则默认情况下会发现Spring boot定义
spring.jpa.open-in-view=true
实际上注册了OpenEntityManagerInViewInterceptor
。将JPA EntityManager
绑定到线程以完成请求的整个处理。
这实际上导致了混乱,请参阅社区issue报道。
基本上,EntityManager打开一个事务,在该事务中声明了@Transaction
注释,或者在您的情况下,OpenEntityManagerViewInterceptor
在每个请求中打开了该注释,并且在做出响应之前保持打开状态。
否。它不是线程安全的,但是EntityManagerFactory
是线程安全的。从EntityManager
获得EntityManagerFatory
。因此,EntityManager价格便宜,并且可以用于单个工作单元。 EntityManagerFactory
通常在应用程序初始化时创建,并在应用程序端关闭。有关Obtaining an EntityManager in a Java SE environment
不。 EntityManager是一个接口,在您自动装配时注入到spring bean中的不是实体管理器本身,而是上下文感知的代理,它将在运行时委派给具体的实体管理器。通常,用于代理的具体类为SharedEntityManagerInvocationHandler
有关整个JPA事务如何进行的更多详细说明。我建议阅读此How Does Spring @Transactional Really Work?
答案 1 :(得分:2)
在Spring Boot中,默认情况下会启用“在视图中打开会话”,这意味着除非您的应用程序逻辑要求,否则您无需在代码中使用@Transaction
。
但是,请注意,启用的OSIV被认为是不好的做法-有关该主题的详细讨论,请参见https://stackoverflow.com/a/37526397/3750108