我用简单的Java模型创建了一个非常简单的Spring应用程序:
@Entity
@Table(name = "TEST_CONTACT")
@Audited
public class TestContact {
private static final long serialVersionUID = -5458933169009292797L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
private Long id;
@Column(name = "FIRST_NAME", nullable = false, length = 128)
@Length(max = 128)
@NotEmpty
private String firstName;
@Column(name = "LAST_NAME", nullable = false, length = 128)
@Length(max = 128)
@NotEmpty
private String lastName;
@Override
public Long getId() {
return id;
}
@Override
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
就像你可以看到我使用Envers跟踪我模型的变化。我还创建了MS SQL服务器脚本来加载模型:
/****** Object: Table [dbo].[TEST_CONTACT] ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[TEST_CONTACT](
[ID] [numeric](19, 0) IDENTITY(1,1) NOT NULL,
[FIRST_NAME] [nvarchar](128) NOT NULL,
[LAST_NAME] [nvarchar](128) NOT NULL
PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
/****** Object: Table [history].[TEST_CONTACT_AUD] ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [history].[TEST_CONTACT_AUD](
[ID] [numeric](19, 0) NOT NULL,
[AUDIT_REVISION] [numeric](19, 0) NOT NULL,
[ACTION_TYPE] [smallint] NULL,
[FIRST_NAME] [nvarchar](128) NOT NULL,
[LAST_NAME] [nvarchar](128) NOT NULL
PRIMARY KEY CLUSTERED
(
[ID] ASC,
[AUDIT_REVISION] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [history].[TEST_CONTACT_AUD] WITH CHECK ADD CONSTRAINT [FK_CONTACT_AUD_REVINFO2] FOREIGN KEY([AUDIT_REVISION])
REFERENCES [history].[REVINFO] ([REV])
GO
ALTER TABLE [history].[TEST_CONTACT_AUD] CHECK CONSTRAINT [FK_CONTACT_AUD_REVINFO2]
GO
我实现了简单的存储库,只是在db:
中创建记录@Repository
public class TestContactReposioryImpl implements TestContactReposiory {
@PersistenceContext
private EntityManager em;
@Override
public void create(TestContact testContact) {
em.persist(testContact);
em.flush();
}
}
我发现在大约20个创建TestContact模型的请求期间,我的数据库被阻止了(MS SQL服务器没有显示任何日志),应用程序也被锁定。我只获得了大约8个记录试图插入的信息(似乎没有插入审计):
DEBUG 2016-12-11 19:16:28,483 org.hibernate.SQL - insert into TEST_CONTACT (FIRST_NAME, LAST_NAME) values (?, ?)
Hibernate: insert into TEST_CONTACT (FIRST_NAME, LAST_NAME) values (?, ?)
DEBUG 2016-12-11 19:16:32,003 org.hibernate.SQL - insert into TEST_CONTACT (FIRST_NAME, LAST_NAME) values (?, ?)
Hibernate: insert into TEST_CONTACT (FIRST_NAME, LAST_NAME) values (?, ?)
DEBUG 2016-12-11 19:16:32,557 org.hibernate.SQL - insert into TEST_CONTACT (FIRST_NAME, LAST_NAME) values (?, ?)
Hibernate: insert into TEST_CONTACT (FIRST_NAME, LAST_NAME) values (?, ?)
DEBUG 2016-12-11 19:16:33,101 org.hibernate.SQL - insert into TEST_CONTACT (FIRST_NAME, LAST_NAME) values (?, ?)
Hibernate: insert into TEST_CONTACT (FIRST_NAME, LAST_NAME) values (?, ?)
DEBUG 2016-12-11 19:16:33,477 org.hibernate.SQL - insert into TEST_CONTACT (FIRST_NAME, LAST_NAME) values (?, ?)
Hibernate: insert into TEST_CONTACT (FIRST_NAME, LAST_NAME) values (?, ?)
DEBUG 2016-12-11 19:16:33,741 org.hibernate.SQL - insert into TEST_CONTACT (FIRST_NAME, LAST_NAME) values (?, ?)
Hibernate: insert into TEST_CONTACT (FIRST_NAME, LAST_NAME) values (?, ?)
DEBUG 2016-12-11 19:16:33,974 org.hibernate.SQL - insert into TEST_CONTACT (FIRST_NAME, LAST_NAME) values (?, ?)
Hibernate: insert into TEST_CONTACT (FIRST_NAME, LAST_NAME) values (?, ?)
DEBUG 2016-12-11 19:16:34,193 org.hibernate.SQL - insert into TEST_CONTACT (FIRST_NAME, LAST_NAME) values (?, ?)
Hibernate: insert into TEST_CONTACT (FIRST_NAME, LAST_NAME) values (?, ?)
这是我发现的:
- 如果我执行大约5个请求,一切正常,
- 如果我评论@Audited
注释,这意味着我不会使用Hibernate envers跟踪,一切正常,我甚至可以运行50个请求来创建TestContact记录
更新:
我注意到它与Apache Commons DBCP及其默认设置有某种关联。如果我更改参数:
然后问题不存在。看起来我找到了解决方案,但我不明白问题的根本原因。
更新2:
附加服务:
@Service
public class TestContactServiceImpl implements TestContactService {
@Autowired
private TestContactRepository testContactRepository;
@PreAuthorize("hasAuthority('PERM_CREATE_TEST_CONTACT')")
@Transactional
@Override
public void create(TestContact testContact) {
testContactRepository.create(testContact);
}
}
附加控制器:
@RestController
@RequestMapping("/secure/sh/testcontact")
public class TestContactController {
@Autowired
private TestContactService testContactService;
@RequestMapping(method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseStatus(value = HttpStatus.CREATED)
public ResponseEntity<Void> create(@RequestBody @Valid TestContact testContact) throws IOException {
testContactService.create(testContact);
HttpHeaders headers = new HttpHeaders();
return new ResponseEntity<>(headers, HttpStatus.CREATED);
}
}
你有什么线索可能有什么不对吗?对我来说似乎就像Hibernate Envers一样,但我没有看到这个问题的根本原因。如果您遇到过这类问题,请告诉我。
答案 0 :(得分:1)
Envers通常会重用与用户的EntityManager
或Session
关联的相同数据库连接,以便在事务提交回调期间刷新审计工作队列。
但是,有时Hibernate Envers可能需要获取额外的Session
,因此需要额外的数据库连接,以便执行特定操作以避免污染原始用户的Session
状态。
从Hibernate代理获取目标实体
只有在修改了包含实体代理的PersistentCollection
时,才会出现此情况。这迫使Envers引入CollectionChangeWorkUnit
并要求打开临时会话以获取代理实体的标识符。
由于您的代码未反映任何正在修改的集合,因此这可能不是问题。
提交审核工作队列
审计工作队列是AuditWorkUnit
的集合,用于描述用户对各种托管持久类型执行的操作,并驱动Hibernate Envers必须执行的操作,以使审计表与用户的架构同步。
工作队列作为对事务的回调参与,该事务在完成之前触发。这通常允许Hibernate Envers将所有AuditWorkUnit
更改推送到审计表,作为修改实体的同一用户事务的一部分,从而使该过程成为原子。
有些情况会影响这一点,迫使Hibernate Envers在另一个连接中推送AuditWorkUnit
更改。
COMMIT
。在这些情况下,Hibernate Envers将打开一个 new Session
,它将在事务完成后释放,启动一个事务,刷新AuditWorkUnit
然后关闭临时会议。这意味着将为这个新打开的会话获取新的数据库连接。
<强>结论强>
鉴于有些情况需要额外的连接来执行各种任务而不会污染用户会话,因此确保连接池的大小合适非常重要。修改连接池设置时,这就是问题消失的原因。
希望有所帮助。