我的Spring应用程序中有以下类:var btn1 = document.getElementById('1');
var btn2 = document.getElementById('2');
var btn3 = document.getElementById('3');
var display1 = btn1.getAttribute('display')
var display2 = btn2.getAttribute('display')
var display3 = btn3.getAttribute('display')
function open() {
if (display1 === ('none')) {
btn1.setAttribute('display', 'block');
} else {
btn1.setAttribute('display', 'none');
}
}
和<img id="1" src="forge.PNG" style="height:320px; display:none; padding:10px">
<img id="2" src="lizard.jpg" style="height:320px; display:none; padding:10px">
<img id="3" src="walkway.jpg" style="height:320px; display:none; padding:10px">
<button onclick="open()">1</button>
<button onclick="document.getElementById('2').style.display='block'">2</button>
<button onclick="document.getElementById('3').style.display='block'">3</button>
。
应用程序启动时TaskService
会运行,但是DevStartup
被视为DevStartup
上的分离实体,这会在启动过程中引发分离实体异常。
Tag("Home")
与此同时,我在tasksRepository.save(task)
类中拥有准确的代码,并使用来自REST控制器的相同参数来调用它。
@Component
@AllArgsConstructor
@Slf4j
@Profile("dev")
public class DevStartup {
private final TagsService tagsService;
private final TagsRepository tagsRepository;
private final TasksRepository tasksRepository;
private final Clock clock;
private final EntityManager entityManager;
@EventListener(ApplicationReadyEvent.class)
public void initializeApplication() {
insertTags();
insertTasks();
}
private void insertTags() {
List<Tag> tags = Arrays.asList(
new Tag("Home")
);
tagsRepository.saveAll(tags);
}
private void insertTasks() {
Task task = new Task("Run a webinar", "Zoom.us", clock.time());
Set<Tag> tagsForTask = Stream.of("Home")
.map(tag -> tagsService.findByName(tag).orElseGet(() -> new Tag(tag)))
.collect(Collectors.toSet());
task.addTags(tagsForTask);
tasksRepository.save(task); // ERROR -> Tag("Home") Entity Detached!!!!!
}
}
这次,TaskService
实体已已附加
addTask("Task title", "Task description", Stream.of("Home").collect(toSet());
两者之间有何区别?为什么实体在一种情况下是分离的,而在另一种情况下是附着的?
我正在将Spring Boot 2.1.9与Hibernate 5和JPA(Spring Data JPA项目)一起使用。
Tag("Home")
只是在呼叫@Service
@RequiredArgsConstructor
public class TasksService {
private final StorageService storageService;
private final TasksRepository tasksRepository;
private final TagsService tagsService;
private final Clock clock;
private final EntityManager entityManager;
public Task addTask(String title, String description, Set<String> tags) {
Task task = new Task(
title,
description,
clock.time()
);
Set<Tag> tagsForTask = tags.stream()
.map(tag -> tagsService.findByName(tag).orElseGet(() -> new Tag(tag)))
.collect(Collectors.toSet());
task.addTags(tagsForTask);
tasksRepository.save(task); // OK -> Tag("Home") Entity Attached
return task;
}
// ...
}
:
TagsService.findByName()
以下是TagsRepository.findByNameContainingIgnoreCase
的跟踪日志。我可以发现从TagService提取Tag之后会话立即关闭,然后再次打开以保存Task(这就是为什么我得到分离的实体异常)。
public interface TagsRepository extends JpaRepository<Tag, Long> {
Optional<Tag> findByNameContainingIgnoreCase(String name);
}
以下是从DevStartup
运行代码时的日志。在获取标签和保存任务之间不会关闭会话。
2020-01-25 23:06:46.774 TRACE 33093 --- [ restartedMain] org.hibernate.internal.SessionImpl : Automatically flushing session
2020-01-25 23:06:46.778 TRACE 33093 --- [ restartedMain] org.hibernate.internal.SessionImpl : SessionImpl#afterTransactionCompletion(successful=true, delayed=false)
2020-01-25 23:06:46.779 TRACE 33093 --- [ restartedMain] org.hibernate.internal.SessionImpl : Closing session [d8094673-13fd-4b7e-af38-4aa01afbcaf7]
2020-01-25 23:06:46.789 TRACE 33093 --- [ restartedMain] org.hibernate.internal.SessionImpl : Opened Session [d2c0c551-6523-466f-ab07-a8c1455c62a4] at timestamp: 1579990006789
2020-01-25 23:06:46.848 DEBUG 33093 --- [ restartedMain] org.hibernate.SQL : select tag0_.id as id1_2_, tag0_.name as name2_2_ from tag tag0_ where upper(tag0_.name) like upper(?) escape ?
Hibernate: select tag0_.id as id1_2_, tag0_.name as name2_2_ from tag tag0_ where upper(tag0_.name) like upper(?) escape ?
2020-01-25 23:06:46.850 TRACE 33093 --- [ restartedMain] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [%Home%]
2020-01-25 23:06:46.850 TRACE 33093 --- [ restartedMain] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [CHAR] - [\]
2020-01-25 23:06:46.855 TRACE 33093 --- [ restartedMain] org.hibernate.internal.SessionImpl : Closing session [d2c0c551-6523-466f-ab07-a8c1455c62a4]
2020-01-25 23:06:46.860 TRACE 33093 --- [ restartedMain] org.hibernate.internal.SessionImpl : Opened Session [6caeb288-a608-4aa7-a5f9-091c69900815] at timestamp: 1579990006860
2020-01-25 23:06:46.867 DEBUG 33093 --- [ restartedMain] org.hibernate.SQL : insert into task (id, uuid, created_at, description, title) values (null, ?, ?, ?, ?)
Hibernate: insert into task (id, uuid, created_at, description, title) values (null, ?, ?, ?, ?)
2020-01-25 23:06:46.868 TRACE 33093 --- [ restartedMain] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [fc9c993b-eccf-45ff-b49a-12498b6e62eb]
2020-01-25 23:06:46.868 TRACE 33093 --- [ restartedMain] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [TIMESTAMP] - [2020-01-25T23:06:46.785455]
2020-01-25 23:06:46.870 TRACE 33093 --- [ restartedMain] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [VARCHAR] - [Zoom.us]
2020-01-25 23:06:46.872 TRACE 33093 --- [ restartedMain] o.h.type.descriptor.sql.BasicBinder : binding parameter [4] as [VARCHAR] - [Run a webinar]
2020-01-25 23:06:46.879 TRACE 33093 --- [ restartedMain] org.hibernate.internal.SessionImpl : SessionImpl#afterTransactionCompletion(successful=false, delayed=false)
2020-01-25 23:06:46.880 TRACE 33093 --- [ restartedMain] org.hibernate.internal.SessionImpl : Closing session [6caeb288-a608-4aa7-a5f9-091c69900815]
2020-01-25 23:06:46.900 ERROR 33093 --- [ restartedMain] o.s.boot.SpringApplication : Application run failed
答案 0 :(得分:2)
很可能您已启用Open Session In View
如果您正在运行Web应用程序,则默认情况下,Spring Boot将注册OpenEntityManagerInViewInterceptor以应用“在视图中打开EntityManager”模式,以允许在Web视图中进行延迟加载。如果您不希望出现这种情况,则应在application.properties中将spring.jpa.open-in-view设置为false。
这将在请求开始时打开会话,并在请求处理完成后关闭会话。拦截器是针对Web请求运行的,但在function timeSince(date) {
var seconds = Math.floor((new Date() - date) / 1000);
var interval = Math.floor(seconds / 31536000);
if (interval > 1) {
return interval + " years";
}
interval = Math.floor(seconds / 2592000);
if (interval > 1) {
return interval + " months";
}
interval = Math.floor(seconds / 86400);
if (interval > 1) {
return interval + " days";
}
interval = Math.floor(seconds / 3600);
if (interval > 1) {
return interval + " hours";
}
interval = Math.floor(seconds / 60);
if (interval > 1) {
return interval + " minutes";
}
return Math.floor(seconds) + " seconds";
}
var aDay = 24*60*60*1000
console.log(timeSince(new Date(Date.now()-aDay)));
console.log(timeSince(new Date(Date.now()-aDay*2)));
事件监听器的情况下不会运行。
当您不依赖于打开的会话时,您可能想使用ApplicationReadyEvent
来延长会话的生存期。参见https://stackoverflow.com/a/24713402/1570854:
在春季,@ Transactional划分的业务交易与休眠会话之间存在一一对应的关系。
也就是说,当通过调用@Transactional方法开始业务交易时,将创建休眠会话(TransactionManager可能将实际创建延迟到首次使用该会话之前)。该方法完成后,将提交或回滚业务事务,这将关闭休眠会话。
许多人认为开放会话是一种反模式:https://vladmihalcea.com/the-open-session-in-view-anti-pattern/