对JPA实体管理器和lazyload感到困惑

时间:2019-03-06 09:19:12

标签: java spring hibernate jpa

我基于 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

我记得几天前我也遇到了这个例外。但是现在我可以在控制器中成功加载惰性数据了。

我主要有以下问题。

  1. 实体管理器何时打开和关闭? (它是在服务中还是在开放中?)
  2. 为什么可以将延迟数据加载到控制器中?
  3. 实体管理器是否是线程安全的? (我用Google搜索,但找不到有用的答案)
  4. 实体经理是单身人士吗? (在上面的代码中,我将实体管理器注入到dao中,尽管我没有使用它,但我使用了spring数据,但是我可以将实体管理器注入到服务,控制器中,发现哈希码不同)

先谢谢了。我在公司中写了这个问题,不允许在公司中将任何代码推送到github。如果可以的话,我认为这将为您带来更多便利,因为带有h2数据库的spring boot项目在本地很容易设置。

2 个答案:

答案 0 :(得分:5)

Spring Boot JPA默认在View中打开会话

如果您使用Appendix A. Common application properties,则默认情况下会发现Spring boot定义

spring.jpa.open-in-view=true

实际上注册了OpenEntityManagerInViewInterceptor。将JPA EntityManager绑定到线程以完成请求的整个处理。 这实际上导致了混乱,请参阅社区issue报道。

何时打开和关闭EntityManager?

基本上,EntityManager打开一个事务,在该事务中声明了@Transaction注释,或者在您的情况下,OpenEntityManagerViewInterceptor在每个请求中打开了该注释,并且在做出响应之前保持打开状态。

实体管理器是线程安全的吗?

否。它不是线程安全的,但是EntityManagerFactory是线程安全的。从EntityManager获得EntityManagerFatory。因此,EntityManager价格便宜,并且可以用于单个工作单元。 EntityManagerFactory通常在应用程序初始化时创建,并在应用程序端关闭。有关Obtaining an EntityManager in a Java SE environment

的详细信息,请参见Hibernate文档。

实体经理是单身人士吗?

不。 EntityManager是一个接口,在您自动装配时注入到spring bean中的不是实体管理器本身,而是上下文感知的代理,它将在运行时委派给具体的实体管理器。通常,用于代理的具体类为SharedEntityManagerInvocationHandler

有关整个JPA事务如何进行的更多详细说明。我建议阅读此How Does Spring @Transactional Really Work?

答案 1 :(得分:2)

在Spring Boot中,默认情况下会启用“在视图中打开会话”,这意味着除非您的应用程序逻辑要求,否则您无需在代码中使用@Transaction

但是,请注意,启用的OSIV被认为是不好的做法-有关该主题的详细讨论,请参见https://stackoverflow.com/a/37526397/3750108