欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 宝鼎售后问题提交 | 后台管理


新闻资讯

MENU

软件开发知识

@Entity@Table(name = student)public class Student { @Id @Ge

点击: 次  来源:宝鼎软件 时间:2017-07-28

原文出处: 常大皮卡丘的专栏

案例描写

本文主要描写了开拓中常见的几个与spring懒加载和事务相关的案例,主要描写常见的利用场景,以及如何规避他们,给出详细的代码。

1. 在新的线程中,会见某个耐久化工具的懒加载属性。
2. 在quartz按时任务中,会见某个耐久化工具的懒加载属性。
3. 在dubbo,motan一类rpc框架中,长途挪用时处事端session封锁的问题。

上面三个案例,其实焦点都是一个问题,就是牵扯到spring事务的打点,而懒加载这个技能,只是较量容易浮现失事务堕落的一个实践,主要用它来激发问题,进而对问题举办思考。

前期筹备

为了能直观的袒暴露第一个案例的问题,我新建了一个项目,回收传统的mvc分层,一个student.Java实体类,一个studentDao.java耐久层,一个studentService.java业务层,一个studentController节制层。

@Entity
@Table(name = "student")
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    private String name;

    getter..setter..
}

耐久层利用springdata,图纸加密,软件开发,框架自动扩展出CURD要领

public interface StudentDao extends JpaRepository<Student, Integer>{
}

service层,先给出普通的挪用要领。用于错误演示。

@Service
public class StudentService {

    @Autowired
    StudentDao studentDao;

    public void testNormalGetOne(){
        Student student = studentDao.getOne(1);
        System.out.println(student.getName());
    }
}

留意:getOne和findOne都是springdata提供的按照id查找单个实体的要领,区别是前者是懒加载,后者是当即加载。我们利用getOne来举办懒加载的尝试,就不消大费周章去写懒加载属性,配置多个实体类了。

controller层,不是简简朴单的挪用,而是在新的线程中挪用。利用controller层来取代单位测试(实际项目中,凡是利用controller挪用service,然后在欣赏器可能http东西中挪用触发,较为利便)

    @RequestMapping("/testNormalGetOne")
    @ResponseBody
    public String testNormalGetOne() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                studentService.testNormalGetOne();
            }
        }).start();
        return "testNormalGetOne";
    }

启动项目后,会见localhost:8080/testNormalGetOne报错如下:

Exception in thread "Thread-6" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:148)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:266)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:68)
    at com.example.transaction.entity.Student_$$_jvste17_0.getName(Student_$$_jvste17_0.java)
    at com.example.transaction.service.StudentService.testNormalGetOne(StudentService.java:71)
    at com.example.transaction.service.StudentService$$FastClassBySpringCGLIB$$f8048714.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:651)
    at com.example.transaction.service.StudentService$$EnhancerBySpringCGLIB$$a6640151.testNormalGetOne(<generated>)
    at com.example.transaction.controller.StudentController$1.run(StudentController.java:71)
    at java.lang.Thread.run(Thread.java:745)

问题阐明

no session说明白什么?

原理很简朴,因为spring的session是和线程绑定的,在整个model->dao->service->controller的挪用链中,这种事务和线程绑定的机制很是契合。而我们呈现的问题正式由于新开启了一个线程,软件开发,这个线程与挪用链的线程不是同一个。

问题办理

我们先利用一种不太优雅的方法办理这个问题。在新的线程中,手动打开session。

public void testNormalGetOne() {
        EntityManagerFactory entityManagerFactory = ApplicationContextProvider.getApplicationContext().getBean(EntityManagerFactory.class);
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityManagerHolder entityManagerHolder = new EntityManagerHolder(entityManager);
        TransactionSynchronizationManager.bindResource(entityManagerFactory, entityManagerHolder);
        Student student = studentDao.getOne(1);
        System.out.println(student.getName());
        TransactionSynchronizationManager.unbindResource(entityManagerFactory);
        EntityManagerFactoryUtils.closeEntityManager(entityManager);
}