Java 计划任务中的Spring事务-分离实体错误

Java 计划任务中的Spring事务-分离实体错误,java,spring,hibernate,transactions,scheduled-tasks,Java,Spring,Hibernate,Transactions,Scheduled Tasks,(我知道类似的问题到处都是,但我找不到合适的解决方案。) 我有一个Spring计划的任务,它通过Spring存储库/Hibernate从数据库读写,包括两个实体之间的多对多关系,需要对延迟初始化的集合进行适当的会话管理 但是,尽管有注释,Spring似乎无法正确管理事务 我做错了什么 (我应该提到在相同的方法上抛出一个@Transactional,该方法与@Scheduled有效,但会导致整个计划任务成为一个事务,而我希望persistbanerCourse是事务性的。) 首先是堆栈跟踪,然后是

(我知道类似的问题到处都是,但我找不到合适的解决方案。)

我有一个Spring计划的任务,它通过Spring存储库/Hibernate从数据库读写,包括两个实体之间的多对多关系,需要对延迟初始化的集合进行适当的会话管理

但是,尽管有注释,Spring似乎无法正确管理事务

我做错了什么

(我应该提到在相同的方法上抛出一个
@Transactional
,该方法与
@Scheduled
有效,但会导致整个计划任务成为一个事务,而我希望
persistbanerCourse
是事务性的。)

首先是堆栈跟踪,然后是相关代码:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: edu.ucdavis.dss.dw.entities.Instructor.courses, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:572) ~[AbstractPersistentCollection.class:4.3.1.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:212) ~[AbstractPersistentCollection.class:4.3.1.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.readElementExistence(AbstractPersistentCollection.java:319) ~[AbstractPersistentCollection.class:4.3.1.Final]
at org.hibernate.collection.internal.PersistentBag.contains(PersistentBag.java:288) ~[PersistentBag.class:4.3.1.Final]
at edu.ucdavis.dss.dw.entities.Instructor.addCourse(Instructor.java:130) ~[Instructor.class:?]
at edu.ucdavis.dss.dw.entities.Course.addInstructor(Course.java:111) ~[Course.class:?]
at edu.ucdavis.dss.dw.entities.Course.addInstructor(Course.java:100) ~[Course.class:?]
at edu.ucdavis.dss.dw.tasks.BannerTasks.persistBannerCourse(BannerTasks.java:184) ~[BannerTasks.class:?]
at edu.ucdavis.dss.dw.tasks.BannerTasks.bannerImport(BannerTasks.java:80) ~[BannerTasks.class:?]
at edu.ucdavis.dss.dw.tasks.BannerTasks$$FastClassBySpringCGLIB$$d1348e2.invoke(<generated>) ~[ReflectUtils.class:?]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[MethodProxy.class:4.0.4.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:640) ~[CglibAopProxy$DynamicAdvisedInterceptor.class:4.0.4.RELEASE]
at edu.ucdavis.dss.dw.tasks.BannerTasks$$EnhancerBySpringCGLIB$$46afeb46.bannerImport(<generated>) ~[ReflectUtils.class:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0]
at java.lang.reflect.Method.invoke(Method.java:483) ~[?:1.8.0]
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65) ~[ScheduledMethodRunnable.class:4.0.4.RELEASE]
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) [DelegatingErrorHandlingRunnable.class:4.0.4.RELEASE]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:1.8.0]
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) [?:1.8.0]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) [?:1.8.0]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) [?:1.8.0]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [?:1.8.0]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [?:1.8.0]
at java.lang.Thread.run(Thread.java:744) [?:1.8.0]
CourseManager.java:

package edu.ucdavis.dss.dw.site;

import edu.ucdavis.dss.dw.entities.Course;
import edu.ucdavis.dss.dw.entities.Department;
import edu.ucdavis.dss.dw.entities.Instructor;
import edu.ucdavis.dss.dw.entities.Term;

import java.util.List;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;

import org.springframework.validation.annotation.Validated;

@Validated
public interface CourseManager
{
List<Instructor> getInstructors();

List<Course> getCourses();

List<Department> getDepartments();

void saveInstructor(@NotNull @Valid Instructor instructor);

void saveCourse(Course course);

void saveDepartment(@NotNull @Valid Department department);

void saveTerm(Term term);

Term getTermById(Long id);

Department getDepartmentByCode(String departmentCode);

Term getTermByCode(String termCode);

Instructor getInstructorByEmployeeId(String employeeId);

Course getCourseByCrnAndTerm(String crn, long termId);

long countCourses();
}
package edu.ucdavis.dss.dw.site;

import edu.ucdavis.dss.dw.entities.Course;
import edu.ucdavis.dss.dw.entities.Department;
import edu.ucdavis.dss.dw.entities.Instructor;
import edu.ucdavis.dss.dw.entities.Term;
import edu.ucdavis.dss.dw.repositories.CourseRepository;
import edu.ucdavis.dss.dw.repositories.DepartmentRepository;
import edu.ucdavis.dss.dw.repositories.InstructorRepository;
import edu.ucdavis.dss.dw.repositories.TermRepository;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.inject.Inject;

import java.util.ArrayList;
import java.util.List;

@Service
public class DefaultCourseManager implements CourseManager
{
@Inject InstructorRepository instructorRepository;
@Inject CourseRepository courseRepository;
@Inject DepartmentRepository departmentRepository;
@Inject TermRepository termRepository;

@Override
@Transactional
public List<Instructor> getInstructors()
{
    return this.toList(this.instructorRepository.findAll());
}

@Override
@Transactional
public List<Course> getCourses()
{
    return this.toList(this.courseRepository.findAll());
}

@Override
@Transactional
public List<Department> getDepartments()
{
    return this.toList(this.departmentRepository.findAll());
}

private <E> List<E> toList(Iterable<E> i)
{
    List<E> list = new ArrayList<>();
    i.forEach(list::add);
    return list;
}

@Override
@Transactional
public void saveInstructor(Instructor instructor)
{
    this.instructorRepository.save(instructor);
}

@Override
@Transactional
public void saveCourse(Course course)
{
    this.courseRepository.save(course);
}

@Override
@Transactional
public void saveDepartment(Department department)
{
    this.departmentRepository.save(department);
}

@Override
@Transactional
public void saveTerm(Term term)
{
    this.termRepository.save(term);
}

@Override
@Transactional
public Term getTermById(Long id)
{
    return this.termRepository.findOne(id);
}

@Override
@Transactional
public Department getDepartmentByCode(String departmentCode)
{
    return this.departmentRepository.getOneByCode(departmentCode);
}

@Override
@Transactional
public Term getTermByCode(String termCode)
{
    return this.termRepository.getOneByCode(termCode);
}

@Override
@Transactional
public Instructor getInstructorByEmployeeId(String employeeId)
{
    return this.instructorRepository.getOneByEmployeeId(employeeId);
}

@Override
@Transactional
public Course getCourseByCrnAndTerm(String crn, long termId)
{
    return this.courseRepository.getOneByCrnAndTermId(crn, termId);
}

@Override
@Transactional
public long countCourses()
{
    return this.courseRepository.count();
}
}
包edu.ucdavis.dss.dw.site;
导入edu.ucdavis.dss.dw.entities.Course;
导入edu.ucdavis.dss.dw.entities.Department;
导入edu.ucdavis.dss.dw.entities.讲师;
导入edu.ucdavis.dss.dw.entities.Term;
导入java.util.List;
导入javax.validation.Valid;
导入javax.validation.constraints.NotNull;
导入org.springframework.validation.annotation.Validated;
@验证
公共接口课程管理员
{
列出getInstructors();
列出getCourses();
列出部门();
无效保存讲师(@NotNull@有效讲师);
无效保存课程(课程);
作废保存部门(@NotNull@有效部门);
无效保存期限(期限);
术语getTermById(长id);
部门getDepartmentByCode(字符串departmentCode);
术语getTermByCode(字符串termCode);
讲师getInstructorByEmployeeId(字符串employeeId);
课程getCourseByCrnAndTerm(字符串crn,长期ID);
长课程();
}
DefaultCourseManager.java:

package edu.ucdavis.dss.dw.site;

import edu.ucdavis.dss.dw.entities.Course;
import edu.ucdavis.dss.dw.entities.Department;
import edu.ucdavis.dss.dw.entities.Instructor;
import edu.ucdavis.dss.dw.entities.Term;

import java.util.List;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;

import org.springframework.validation.annotation.Validated;

@Validated
public interface CourseManager
{
List<Instructor> getInstructors();

List<Course> getCourses();

List<Department> getDepartments();

void saveInstructor(@NotNull @Valid Instructor instructor);

void saveCourse(Course course);

void saveDepartment(@NotNull @Valid Department department);

void saveTerm(Term term);

Term getTermById(Long id);

Department getDepartmentByCode(String departmentCode);

Term getTermByCode(String termCode);

Instructor getInstructorByEmployeeId(String employeeId);

Course getCourseByCrnAndTerm(String crn, long termId);

long countCourses();
}
package edu.ucdavis.dss.dw.site;

import edu.ucdavis.dss.dw.entities.Course;
import edu.ucdavis.dss.dw.entities.Department;
import edu.ucdavis.dss.dw.entities.Instructor;
import edu.ucdavis.dss.dw.entities.Term;
import edu.ucdavis.dss.dw.repositories.CourseRepository;
import edu.ucdavis.dss.dw.repositories.DepartmentRepository;
import edu.ucdavis.dss.dw.repositories.InstructorRepository;
import edu.ucdavis.dss.dw.repositories.TermRepository;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.inject.Inject;

import java.util.ArrayList;
import java.util.List;

@Service
public class DefaultCourseManager implements CourseManager
{
@Inject InstructorRepository instructorRepository;
@Inject CourseRepository courseRepository;
@Inject DepartmentRepository departmentRepository;
@Inject TermRepository termRepository;

@Override
@Transactional
public List<Instructor> getInstructors()
{
    return this.toList(this.instructorRepository.findAll());
}

@Override
@Transactional
public List<Course> getCourses()
{
    return this.toList(this.courseRepository.findAll());
}

@Override
@Transactional
public List<Department> getDepartments()
{
    return this.toList(this.departmentRepository.findAll());
}

private <E> List<E> toList(Iterable<E> i)
{
    List<E> list = new ArrayList<>();
    i.forEach(list::add);
    return list;
}

@Override
@Transactional
public void saveInstructor(Instructor instructor)
{
    this.instructorRepository.save(instructor);
}

@Override
@Transactional
public void saveCourse(Course course)
{
    this.courseRepository.save(course);
}

@Override
@Transactional
public void saveDepartment(Department department)
{
    this.departmentRepository.save(department);
}

@Override
@Transactional
public void saveTerm(Term term)
{
    this.termRepository.save(term);
}

@Override
@Transactional
public Term getTermById(Long id)
{
    return this.termRepository.findOne(id);
}

@Override
@Transactional
public Department getDepartmentByCode(String departmentCode)
{
    return this.departmentRepository.getOneByCode(departmentCode);
}

@Override
@Transactional
public Term getTermByCode(String termCode)
{
    return this.termRepository.getOneByCode(termCode);
}

@Override
@Transactional
public Instructor getInstructorByEmployeeId(String employeeId)
{
    return this.instructorRepository.getOneByEmployeeId(employeeId);
}

@Override
@Transactional
public Course getCourseByCrnAndTerm(String crn, long termId)
{
    return this.courseRepository.getOneByCrnAndTermId(crn, termId);
}

@Override
@Transactional
public long countCourses()
{
    return this.courseRepository.count();
}
}
包edu.ucdavis.dss.dw.site;
导入edu.ucdavis.dss.dw.entities.Course;
导入edu.ucdavis.dss.dw.entities.Department;
导入edu.ucdavis.dss.dw.entities.讲师;
导入edu.ucdavis.dss.dw.entities.Term;
导入edu.ucdavis.dss.dw.repositories.CourseRepository;
导入edu.ucdavis.dss.dw.repositories.DepartmentRepository;
导入edu.ucdavis.dss.dw.repositories.Instructor或repository;
导入edu.ucdavis.dss.dw.repositories.TermRepository;
导入org.springframework.stereotype.Service;
导入org.springframework.transaction.annotation.Transactional;
导入javax.inject.inject;
导入java.util.ArrayList;
导入java.util.List;
@服务
公共类DefaultCourseManager实现CourseManager
{
@注射讲师或保存讲师或保存员;
@注射CourseRepository CourseRepository;
@注入DepartmentRepository DepartmentRepository;
@注入术语库术语库;
@凌驾
@交易的
公共列表getInstructors()
{
返回this.toList(this.instructorRepository.findAll());
}
@凌驾
@交易的
公开课程名单
{
返回this.toList(this.courseRepository.findAll());
}
@凌驾
@交易的
公共部门名单(
{
返回this.toList(this.departmentRepository.findAll());
}
私人名单收费表(Iterable i)
{
列表=新的ArrayList();
i、 forEach(列表::add);
退货清单;
}
@凌驾
@交易的
公共讲师(讲师讲师)
{
this.instructor repository.save(讲师);
}
@凌驾
@交易的
公共课程(课程)
{
this.courseRepository.save(课程);
}
@凌驾
@交易的
公共服务部(部门)
{
这个.departmentRepository.save(department);
}
@凌驾
@交易的
公共无效保存期限(期限)
{
此.termRepository.save(术语);
}
@凌驾
@交易的
公共术语getTermById(长id)
{
返回此.termRepository.findOne(id);
}
@凌驾
@交易的
公共部门getDepartmentByCode(字符串departmentCode)
{
返回此.departmentRepository.getOneByCode(departmentCode);
}
@凌驾
@交易的
公共术语getTermByCode(字符串termCode)
{
返回此.termRepository.getOneByCode(termCode);
}
@凌驾
@交易的
公共讲师getInstructorByEmployeeId(字符串employeeId)
{
返回此.instructor repository.getOneByEmployeeId(employeeId);
}
@凌驾
@交易的
公共课程getCourseByCrnAndTerm(字符串crn,长期ID)
{
返回此.courseRepository.getOneByCrnAndTermId(crn,termId);
}
@凌驾
@交易的
公共长期课程()
{
返回此.courseRepository.count();
}
}

您似乎偶然发现了Spring AOP抽象的一个经典问题。 虽然您已经用
@Transactional
注释了您的
persistBannerCourse
方法,但是您是从同一个类中调用它的。这意味着处理事务代码的代理永远不会被调用

查看相关的SO问题

最好的解决方案是重构代码并将
persistBannerCourse
移动到另一个类。
另一个解决方案是描述的

谢谢,这很有意义。我继续进行重构。我还发现可以使用TransactionTemplates,但这感觉有点太手动了。
package edu.ucdavis.dss.dw.site;

import edu.ucdavis.dss.dw.entities.Course;
import edu.ucdavis.dss.dw.entities.Department;
import edu.ucdavis.dss.dw.entities.Instructor;
import edu.ucdavis.dss.dw.entities.Term;
import edu.ucdavis.dss.dw.repositories.CourseRepository;
import edu.ucdavis.dss.dw.repositories.DepartmentRepository;
import edu.ucdavis.dss.dw.repositories.InstructorRepository;
import edu.ucdavis.dss.dw.repositories.TermRepository;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.inject.Inject;

import java.util.ArrayList;
import java.util.List;

@Service
public class DefaultCourseManager implements CourseManager
{
@Inject InstructorRepository instructorRepository;
@Inject CourseRepository courseRepository;
@Inject DepartmentRepository departmentRepository;
@Inject TermRepository termRepository;

@Override
@Transactional
public List<Instructor> getInstructors()
{
    return this.toList(this.instructorRepository.findAll());
}

@Override
@Transactional
public List<Course> getCourses()
{
    return this.toList(this.courseRepository.findAll());
}

@Override
@Transactional
public List<Department> getDepartments()
{
    return this.toList(this.departmentRepository.findAll());
}

private <E> List<E> toList(Iterable<E> i)
{
    List<E> list = new ArrayList<>();
    i.forEach(list::add);
    return list;
}

@Override
@Transactional
public void saveInstructor(Instructor instructor)
{
    this.instructorRepository.save(instructor);
}

@Override
@Transactional
public void saveCourse(Course course)
{
    this.courseRepository.save(course);
}

@Override
@Transactional
public void saveDepartment(Department department)
{
    this.departmentRepository.save(department);
}

@Override
@Transactional
public void saveTerm(Term term)
{
    this.termRepository.save(term);
}

@Override
@Transactional
public Term getTermById(Long id)
{
    return this.termRepository.findOne(id);
}

@Override
@Transactional
public Department getDepartmentByCode(String departmentCode)
{
    return this.departmentRepository.getOneByCode(departmentCode);
}

@Override
@Transactional
public Term getTermByCode(String termCode)
{
    return this.termRepository.getOneByCode(termCode);
}

@Override
@Transactional
public Instructor getInstructorByEmployeeId(String employeeId)
{
    return this.instructorRepository.getOneByEmployeeId(employeeId);
}

@Override
@Transactional
public Course getCourseByCrnAndTerm(String crn, long termId)
{
    return this.courseRepository.getOneByCrnAndTermId(crn, termId);
}

@Override
@Transactional
public long countCourses()
{
    return this.courseRepository.count();
}
}