【问题标题】:Audit + spring-boot-data-jpa + Hibernate + JPA 2 transactional exception审计 + spring-boot-data-jpa + Hibernate + JPA 2 事务异常
【发布时间】:2018-02-04 15:08:06
【问题描述】:

我苦苦挣扎了 2 天,试图解决这个问题,但我不知道我还能做什么。在我将@Audit 包含在实体中之前,我的系统运行良好。当然,我在 .pom 文件中包含了 Hibernate Envers。

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-envers</artifactId>
</dependency>

我已经尝试过这些帖子:

Hibernare envers Audit with Spring data JPA and Spring Boot

spring-data-envers Hibernate java.lang.NoSuchMethodError: org.hibernate.engine.spi.SessionImplementor.getTransactionCoordinator

Hibernate-envers throwing exception when Deleting entity with a collection using CrudRepository

但不幸的是,根本没有成功:(

我检查了我的maven repository,发现无论我在 .pom 文件中设置什么版本,它都在使用 hibernate 5.0.12.Final 版本的 hibernate-core、hibernate-entitymanager 和 hibernate-envers。

这是我的 .pom 文件的依赖项:

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-envers</artifactId>
  </dependency>
  <dependency>
    <groupId>org.hibernate.javax.persistence</groupId>
    <artifactId>hibernate-jpa-2.1-api</artifactId>
    <version>1.0.0.Final</version>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jersey</artifactId>
  </dependency>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web-services</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>4.2.3.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>4.2.3.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.7.0</version>
  </dependency>
  <dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.1.3</version>
  </dependency>
  <dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>2.6.1</version><!--$NO-MVN-MAN-VER$-->
  </dependency>

  <dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-core</artifactId>
    <version>1.5.13</version>
  </dependency>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework.restdocs</groupId>
    <artifactId>spring-restdocs-mockmvc</artifactId>
    <scope>test</scope>
  </dependency>

  <dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.16</version>
  </dependency>
</dependencies>

这里是监听类

public class BRAuditEnversListener implements RevisionListener {
  protected static final Logger log = Logger.getLogger("com.zerofila.web");

  @Autowired
  private UserService service;

  @Override
  public void newRevision(Object revisionEntity) {        
    BRAuditRevisionEntity customRevisionEntity = (BRAuditRevisionEntity) revisionEntity;
    customRevisionEntity.setUsername( service.getAuthenticatedUser().getEmail() );
  }
}

Revinfo 类:

@Entity
@Table(name = "revinfo")
@RevisionEntity(BRAuditEnversListener.class)
public class BRAuditRevisionEntity implements Serializable {
  private static final long serialVersionUID = 1L;

  @RevisionNumber
  private int id;
  @RevisionTimestamp
  private long timestamp;
  private String username;

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  public int getId() {
    return id;
  }

  public void setId(int id) {
    this.id = id;
  }

  @Transient
  public Date getRevisionDate() {
    return new Date(timestamp);
  }

  public long getTimestamp() {
    return timestamp;
  }

  public void setTimestamp(long timestamp) {
    this.timestamp = timestamp;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
        return true;
    }
    if (!(o instanceof DefaultRevisionEntity)) {
        return false;
    }

    DefaultRevisionEntity that = (DefaultRevisionEntity) o;

    if (id != that.getId()) {
        return false;
    }
    if (timestamp != that.getTimestamp()) {
        return false;
    }

    return true;
  }

  @Override
  public int hashCode() {
    int result;
    result = id;
    result = 31 * result + (int) (timestamp ^ (timestamp >>> 32));
    return result;
  }

  @Override
  public String toString() {
    return "DefaultRevisionEntity(id = " + id + ", revisionDate = " + DateFormat.getDateTimeInstance().format(getRevisionDate()) + ")";
  }

  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }
}

调用插入方法的Web服务端点:

    @Override
@RequestMapping(value = "/create", method = RequestMethod.POST)
public ResponseEntity<Local> create(@ApiParam(value = "Local json stream resource", required = true) @Valid @RequestBody Local local) {
    Local created = service.insert(local);

    if (null == created)
        return new ResponseEntity<>(HttpStatus.NOT_MODIFIED);

    return new ResponseEntity<Local>(created, HttpStatus.CREATED);
}

这是一个抛出异常的方法:

@Transactional
@Override
public Local insert(Local local) {
    return repository.save( local );
}

最后,例外(很短,顺便说一句):

2017-08-26 20:18:52.006 ERROR 2784 --- [nio-8080-exec-4] org.hibernate.AssertionFailure           : HHH000099: an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): java.lang.NullPointerException
2017-08-26 20:18:52.009  WARN 2784 --- [nio-8080-exec-4] .m.m.a.ExceptionHandlerExceptionResolver : Resolved exception caused by Handler execution: org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction

我需要配置一些特殊的东西吗?也许添加一些注释?知道我能做什么吗?

【问题讨论】:

  • 目前尚不清楚保存Local 的实例如何触发审计事件(然后导致上述异常)。此外,对于一个合适的 Spring Boot 项目来说,POM 文件太复杂了(例如,为什么要包含启动数据 JPA 和 JPA API;它们已经被 Spring Boot 启动器导入了)。您应该首先尝试使用现有的 Spring Boot 项目实施审计(在 start.spring.io 创建一个),而不引入任何与审计无关的依赖项。当一切正常时,逐渐添加其他部分。
  • 谢谢@manish,我会检查所有这些。奇怪的是(正如我所说)“我的系统运行良好,直到我将 @Audit 包含在实体中。”当然,hibernate-envers 作为依赖项。最重要的是,您是否建议我在我的 .pom 文件中排除这些条目:&lt;artifactId&gt;spring-boot-starter-data-jpa&lt;/artifactId&gt;&lt;artifactId&gt;hibernate-jpa-2.1-api&lt;/artifactId&gt;?顺便说一句,我在问题中添加了调用插入的方法。谢谢
  • 我建议创建一个仅包含 spring-boot-starterhibernate-envers 依赖项的示例项目,并使用它来测试审计要求。

标签: hibernate spring-boot spring-data-jpa jpa-2.0


【解决方案1】:

经过很长时间(大约 3 天)的尝试、测试和分析所有来源,我发现了错误并因此找到了解决方案。

由于此条目而发生错误:

@Autowired
private UserService service;

由于某种原因,service.getAuthenticatedUser() 返回 null,然后抛出异常。

我的解决方案是直接从SecurityContextHolder 获取经过身份验证的用户,如下所示:

public class BRAuditEnversListener implements RevisionListener {

    @Override
    public void newRevision(Object revisionEntity) {        
        BRAuditRevisionEntity customRevisionEntity = (BRAuditRevisionEntity) revisionEntity;
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        customRevisionEntity.setUsername( (String)authentication.getPrincipal() );
    }
}

现在,一切正常:)

【讨论】:

    猜你喜欢
    • 2017-07-09
    • 1970-01-01
    • 2015-04-20
    • 1970-01-01
    • 2021-10-03
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 2015-01-20
    相关资源
    最近更新 更多