英文:
Transactional annotation doesn't solve org.hibernate.LazyInitializationException
问题
我正在编写一个简单的Spring Data JPA应用程序。我使用MySQL数据库。有两个简单的表:
- Department(部门)
- 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:
- Department
- 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 = "department")
List<Employee> 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<Department, Long> {}
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {}
There is a DepartmentService class:
@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);
}
}
}
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论