Transactional注解无法解决org.hibernate.LazyInitializationException。

huangapple go评论67阅读模式
英文:

Transactional annotation doesn't solve org.hibernate.LazyInitializationException

问题

我正在编写一个简单的Spring Data JPA应用程序。我使用MySQL数据库。有两个简单的表:

  1. Department(部门)
  2. Employee(员工)

每个员工都在某个部门工作(Employee.department_id)。

我有两个实体类:

@Entity
public class Department {
	@GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
	private Long id;

    @Basic(fetch = FetchType.LAZY)
	@OneToMany(mappedBy = "department")
    List<Employee> employees;
}

@Entity
public class Person {
	@GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
	private Long id;

    @ManyToOne
	@JoinColumn
	private Department department;
}

仓库:

@Repository
public interface DepartmentRepository extends JpaRepository<Department, Long> {}

@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {}

有一个DepartmentService类:

@Service
@Transactional
public class DepartmentService {
    @Autowired
    DepartmentRepository departmentRepository;

    @Transactional
	List<Department> getAll() {
        departmentRepository.findAll();
    }

    @Transactional
	void getEmployees() {
        for(Department department: getAll()) {
            List<Employee> employees = department.getEmployees();
            for(Employee employee: employees)
                System.out.println(employee);
        }
    }
}

还有一个主类(我不需要web,所以我使用CommandLineRunner将应用程序作为控制台应用程序运行):

@SpringBootApplication
public class EmployeeDB implements CommandLineRunner {
    @Autowired
    DepartmentService departmentService;

    public static void main(String[] args) {
		SpringApplication.run(EmployeeDB.class, args);
    }

    @Override
	public void run(String... args) throws Exception {
        departmentService.getEmployees();
    }
}

我在类DepartmentService之前添加了@Transactional,并且还在所有的方法之前添加了,但我仍然不断收到以下错误:

 org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: employeeDB.Department.employees, could not initialize proxy - no Session

堆栈跟踪:

Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: employeeDB.Department.employees, could not initialize proxy - no Session
	at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:602) ~[hibernate-core-5.4.2.Final.jar:5.4.2.Final]
	at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:217) ~[hibernate-core-5.4.2.Final.jar:5.4.2.Final]
	at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:581) ~[hibernate-core-5.4.2.Final.jar:5.4.2.Final]
	at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:148) ~[hibernate-core-5.4.2.Final.jar:5.4.2.Final]
	at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:303) ~[hibernate-core-5.4.2.Final.jar:5.4.2.Final]
	at employeeDB.DepartmentService.getEmployees(DepartmentService.java:25) ~[main/:na]
	at employeeDB.EmployeeDB.run(EmployeeDB.java:41) [main/:na]
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:795) [spring-boot-2.3.3.RELEASE.jar:2.3.3.RELEASE]
	... 5 common frames omitted

为什么会出现这个错误?我该如何解决?

英文:

I'm writing a simple Spring Data JPA application. I use MySQL database. There are two simple tables:

  1. Department
  2. Employee

Each employee works in some department (Employee.department_id).

I have two entity classes:

@Entity
public class Department {
	@GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
	private Long id;

    @Basic(fetch = FetchType.LAZY)
	@OneToMany(mappedBy = &quot;department&quot;)
    List&lt;Employee&gt; employees;
}

@Entity
public class Person {
	@GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
	private Long id;

    @ManyToOne
	@JoinColumn
	private Department department;
}

The repositories:

@Repository
public interface DepartmentRepository extends JpaRepository&lt;Department, Long&gt; {}

@Repository
public interface EmployeeRepository extends JpaRepository&lt;Employee, Long&gt; {}

There is a DepartmentService class:

@Service
@Transactional
public class DepartmentService {
    @Autowired
    DepartmentRepository departmentRepository;

    @Transactional
	List&lt;Department&gt; getAll() {
        departmentRepository.findAll();
    }

    @Transactional
	void getEmployees() {
        for(Department department: getAll()) {
            List&lt;Employee&gt; employees = department.getEmployees();
            for(Employee employee: employees)
                System.out.println(employee);
        }
    }
}

And the main class (I don't need web, so I use CommandLineRunner to run the application as a console application):

@SpringBootApplication
public class EmployeeDB implements CommandLineRunner {
    @Autowired
    DepartmentService departmentService;

    public static void main(String[] args) {
		SpringApplication.run(EmployeeDB.class, args);
    }

    @Override
	public void run(String... args) throws Exception {
        departmentService.getEmployees();
    }
}

I added @Transactional before the class DepartmentService and also before all its methods, but I still keep getting the following error:

 org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: employeeDB.Department.employees, could not initialize proxy - no Session

Stacktrace:

Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: employeeDB.Department.employees, could not initialize proxy - no Session
	at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:602) ~[hibernate-core-5.4.2.Final.jar:5.4.2.Final]
	at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:217) ~[hibernate-core-5.4.2.Final.jar:5.4.2.Final]
	at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:581) ~[hibernate-core-5.4.2.Final.jar:5.4.2.Final]
	at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:148) ~[hibernate-core-5.4.2.Final.jar:5.4.2.Final]
	at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:303) ~[hibernate-core-5.4.2.Final.jar:5.4.2.Final]
	at employeeDB.DepartmentService.getEmployees(DepartmentService.java:25) ~[main/:na]
	at employeeDB.EmployeeDB.run(EmployeeDB.java:41) [main/:na]
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:795) [spring-boot-2.3.3.RELEASE.jar:2.3.3.RELEASE]
	... 5 common frames omitted

Why do I get this error? How can I solve it?

答案1

得分: 3

汇总以上所有的答案和评论:

出现这种情况是因为方法 DepartmentService::getEmployees 不是公共的。

我在 getEmployees() 之前添加了 public,问题得到了解决。

@Transactional 注解由AOP提供支持,因此只有公共方法才能被代理处理。

这意味着 @Transactional 实际上未应用于此方法,会话在读取延迟集合时已关闭。

英文:

Summarizing all the answers and comments above:

This happens because method DepartmentService::getEmployees is not public.

> I added public before getEmployees() and this sloved the problem.

@Transactional annotation is powered by AOP, then only public methods could be handled by proxy.

This means that @Transactional is not actually applied to this method and the session is already closed an the moment of reading lazy collection.

答案2

得分: 2

@Transactional 不应该用于获取延迟加载的实体,这会导致 n+1 选择问题

您需要使用适当的映射或查询,例如在 JPQL 中:

select d
from Department d
left join fetch d.employees
英文:

@Transactional is not supposed to be used to fetch lazily loaded entities, this will lead to n+1 select problem.

You need to use proper mapping or query instead, for example in jpql:

select d
from Department d
left join fetch d.employees

huangapple
  • 本文由 发表于 2020年9月1日 01:05:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/63675153.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定