Hibernate envers会导致数据库锁定

时间:2016-12-11 19:12:54

标签: spring hibernate sql-server-2008 hibernate-envers

我用简单的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及其默认设置有某种关联。如果我更改参数:

  • maxActive = 100
  • maxIdle = 100
  • maxOpenPreparedStatements = 100

然后问题不存在。看起来我找到了解决方案,但我不明白问题的根本原因。

更新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一样,但我没有看到这个问题的根本原因。如果您遇到过这类问题,请告诉我。

1 个答案:

答案 0 :(得分:1)

Envers通常会重用与用户的EntityManagerSession关联的相同数据库连接,以便在事务提交回调期间刷新审计工作队列。

但是,有时Hibernate Envers可能需要获取额外的Session,因此需要额外的数据库连接,以便执行特定操作以避免污染原始用户的Session状态。

从Hibernate代理获取目标实体

只有在修改了包含实体代理的PersistentCollection时,才会出现此情况。这迫使Envers引入CollectionChangeWorkUnit并要求打开临时会话以获取代理实体的标识符。

由于您的代码未反映任何正在修改的集合,因此这可能不是问题。

提交审核工作队列

审计工作队列是AuditWorkUnit的集合,用于描述用户对各种托管持久类型执行的操作,并驱动Hibernate Envers必须执行的操作,以使审计表与用户的架构同步。

工作队列作为对事务的回调参与,该事务在完成之前触发。这通常允许Hibernate Envers将所有AuditWorkUnit更改推送到审计表,作为修改实体的同一用户事务的一部分,从而使该过程成为原子。

有些情况会影响这一点,迫使Hibernate Envers在另一个连接中推送AuditWorkUnit更改。

  1. 用户会话的刷新模式设置为COMMIT
  2. 用户会话已关闭。
  3. 在这些情况下,Hibernate Envers将打开一个 new Session,它将在事务完成后释放,启动一个事务,刷新AuditWorkUnit然后关闭临时会议。这意味着将为这个新打开的会话获取新的数据库连接。

    <强>结论

    鉴于有些情况需要额外的连接来执行各种任务而不会污染用户会话,因此确保连接池的大小合适非常重要。修改连接池设置时,这就是问题消失的原因。

    希望有所帮助。