SpringBoot + JPA多线程下懒加载No session怎么处理及其解决及其完整代码
时间: 2024-01-22 18:01:55 浏览: 255
在多线程下,如果使用懒加载(Lazy Loading)加载实体类的关联对象,可能会出现 No session 异常,因为每个线程都有自己的 Session,当一个线程中的 Session 关闭后,另一个线程再去访问同一个实体类的关联对象时,就会出现 No session 异常。
解决这个问题的方法是在每个线程中打开一个 Session,进行操作后再关闭。在 Spring Boot 中,可以使用 @Transactional 注解来管理事务和 Session。
以下是一个完整的示例代码:
实体类:
```java
@Entity
public class User {
@Id
private Long id;
private String name;
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<Order> orders;
// getter 和 setter 略
}
@Entity
public class Order {
@Id
private Long id;
private String orderNo;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
// getter 和 setter 略
}
```
DAO 层:
```java
@Repository
public class UserRepository {
@Autowired
private EntityManager entityManager;
public User findById(Long id) {
return entityManager.find(User.class, id);
}
}
@Repository
public class OrderRepository {
@Autowired
private EntityManager entityManager;
public Order findById(Long id) {
return entityManager.find(Order.class, id);
}
}
```
Service 层:
```java
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private OrderRepository orderRepository;
@Transactional
public void test() {
User user = userRepository.findById(1L);
System.out.println(user.getName());
List<Order> orders = user.getOrders(); // 懒加载
for (Order order : orders) {
System.out.println(order.getOrderNo());
}
}
}
```
在多线程环境下,可以通过创建一个新的 EntityManager 来避免 No session 异常:
```java
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private OrderRepository orderRepository;
@Transactional
public void test() {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
executorService.submit(() -> {
EntityManager entityManager = null;
try {
entityManager = JpaUtils.getEntityManager();
User user = userRepository.findById(1L);
System.out.println(user.getName());
List<Order> orders = user.getOrders(); // 懒加载
for (Order order : orders) {
System.out.println(order.getOrderNo());
}
} finally {
if (entityManager != null && entityManager.isOpen()) {
entityManager.close();
}
}
});
}
executorService.shutdown();
try {
executorService.awaitTermination(1, TimeUnit.HOURS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
```
其中,JpaUtils.getEntityManager() 方法用于创建一个新的 EntityManager:
```java
public class JpaUtils {
private static final EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("default");
public static EntityManager getEntityManager() {
return entityManagerFactory.createEntityManager();
}
}
```
需要在 application.yml 文件中配置数据库连接信息和 JPA 相关的属性:
```yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
show_sql: true
format_sql: true
hbm2ddl.auto: update
open-in-view: false
generate-ddl: true
hibernate:
ddl-auto: update
```
在以上示例代码中,使用了线程池来模拟多线程请求。在每个线程中,都创建了一个新的 EntityManager,并在 finally 块中关闭。这样可以避免 No session 异常。
阅读全文