解决Quartz定时器中查询懒加载数据no session的问题

相信大家在web开发过程中一定遇到过一种情况,Class班级类一对多关联一个Student学生类,所以为了性能考虑,配置一个lazy-init=true,然后在前台页面需要访问懒加载数据时需要额外配置一个OpenSessionInViewFilter,但是可能并不知道这个过滤器到底做了什么,遇到Quartz定时中访问懒加载问题配置这个filter可没有用了,下面来讨论下,怎么解决这个问题。

实验之前先构造两个实体类,实现懒加载

@Entity
@Table(name = "clazz")
public class Clazz {
    @Id
    @GenericGenerator(name = "PKUUID", strategy = "uuid2")
    @GeneratedValue(generator = "PKUUID")
    @Column(length = 36)
    private String id;
    private String clazzName;
    private String clazzNumber;

    @OneToMany(fetch = FetchType.LAZY,mappedBy = "clazz",cascade = CascadeType.ALL)
    private List<Student> students;

    get/set...
}

Student类省略,我们现在数据手动插入一条记录,待会我们在定时器里面查询他。

定时任务代码如下:

/**
 * Created by xujingfeng on 2016/11/3
 */
public class DemoJob {

    @Autowired
    ClazzDao clazzDao;

    public static final Logger logger = LoggerFactory.getLogger(DemoJob.class);

    public DemoJob() {
        System.out.println("DemoJob=====>init");
    }

    public void run() {

        Clazz clazz = clazzDao.findOne("ba9071fb-f2da-481a-9a90-371022cda195");
        System.out.println(clazz.getClazzName());
        System.out.println(clazz.getClazzNumber());
        System.out.println(clazz.getStudents());
        logger.info("run============================@" + new Date());

    }

}

我们先不做额外配置看看运行结果

计算机
131
2016-11-04 10:16:50.213 ERROR 6692 --- [ryBean_Worker-1] org.quartz.core.JobRunShell              : Job job_work.job_name threw an unhandled Exception: 

org.springframework.scheduling.quartz.JobMethodInvocationFailedException: Invocation of method 'run' on target class [class com.example.quartz.job.DemoJob] failed; nested exception is org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.quartz.common.entity.Clazz.students, could not initialize proxy - no Session

可以看到,非懒加载数据可以顺利查出来,但是懒加载数据会报no session

很明显,session已经关闭了。

  • 解决方案1
    关闭懒加载,好吧,当我没讲
  • 解决方案2
    给定时器调度的方案用事务包裹
@Transactional
public void run() {

        Clazz clazz = clazzDao.findOne("ba9071fb-f2da-481a-9a90-371022cda195");
        System.out.println(clazz.getClazzName());
        System.out.println(clazz.getClazzNumber());
        System.out.println(clazz.getStudents());
        logger.info("run============================@" + new Date());

    }

原理就是,用事务包裹之后,会当做一个整体,没有提交时session不会交给SessionFactory管理

  • 解决方案3
    既然session关闭了,那就打开它就行了,类似于OpensessionInView,不过要取决于你的持久化方案来决定到底打开什么,一般是SessionFactory或者EntityManager,由于我是采用的JPA,所以使用后者

配置一个任务监听器,用于打开session

public class OpenEntityManagerJobListener extends JobListenerSupport implements ApplicationContextAware {

    @Override
    public String getName() {
        return "OpenEntityManagerJobListener";
    }

    EntityManagerFactory entityManagerFactory;

    @Override
    public void jobToBeExecuted(JobExecutionContext context) {
        entityManagerFactory = applicationContext.getBean(EntityManagerFactory.class);
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityManagerHolder emHolder = new EntityManagerHolder(entityManager);
        TransactionSynchronizationManager.bindResource(entityManagerFactory, emHolder);
    }

    @Override
    public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
        EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager.unbindResource(entityManagerFactory);
        EntityManagerFactoryUtils.closeEntityManager(emHolder.getEntityManager());
    }


    ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        if(this.applicationContext ==null) throw new RuntimeException("applicationContext is null");
    }
}

之后记得给调度器注册全局任务监听器,对所有任务监听

//调度工厂
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() {
        SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
        factoryBean.setTriggers(triggerFactoryBeans().getObject());
        factoryBean.setGlobalJobListeners(openEntityManagerJobListener());
        return factoryBean;
    }

再来试试运行一下定时任务

2016-11-04 10:29:12.234  INFO 11188 --- [           main] com.example.quartz.CommonApp             : Started CommonApp in 12.067 seconds (JVM running for 12.675)
计算机
131
Hibernate: select students0_.clazz_id as clazz_id4_1_0_, students0_.id as id1_1_0_, students0_.id as id1_1_1_, students0_.clazz_id as clazz_id4_1_1_, students0_.stu_name as stu_name2_1_1_, students0_.stu_numer as stu_numer3_1_1_ from student students0_ where students0_.clazz_id=?
[com.example.quartz.common.entity.Student@2cd010f8, com.example.quartz.common.entity.Student@100a20c4]
2016-11-04 10:29:12.336  INFO 11188 --- [ryBean_Worker-1] com.example.quartz.job.DemoJob           : run============================@Date

打完收工

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值