java - 我如何使用 Hibernate/JPA 在插入/更新/删除之前告诉数据库用户是谁?

标签 java spring oracle hibernate jpa

总结(详情如下):

我想在使用 Spring/JPA 堆栈保存/更新/删除任何实体之前进行存储过程调用。

无聊的细节:

我们有一个 Oracle/JPA(Hibernate)/Spring MVC(带有 Spring Data repos)应用程序,它被设置为使用触发器将一些表的历史记录到一组历史表中(我们要审计的每个表一个历史表) ).这些实体中的每一个都有一个 modifiedByUser,通过在更新或插入时扩展 EmptyInterceptor 的类来设置。当触发器归档任何插入或更新时,它可以使用此列轻松查看谁进行了更改(我们感兴趣的是哪个应用程序用户,而不是数据库用户)。问题是对于删除,我们不会从执行的 SQL 中获取最后修改的信息,因为它只是一个普通的 delete from x where y

为了解决这个问题,我们想执行一个存储过程来告诉数据库哪个应用程序用户在执行任何操作之前登录。然后,审计触发器会在删除发生时查看此值,并使用它来记录执行删除的人员。

有没有办法拦截开始事务或其他一些执行SQL或存储过程的方法来告诉数据库什么用户正在执行插入/更新/删除,这些插入/更新/删除将在其余部分之前发生在事务中操作发生了吗?

我不太了解有关数据库端如何工作的详细信息,但如有必要,我可以获得更多信息。要点是存储过程将创建一个上下文来保存 session 变量,触发器将在删除时查询该上下文以获取用户 ID。

最佳答案

从数据库端,这里有一些讨论:

https://docs.oracle.com/cd/B19306_01/network.102/b14266/apdvprxy.htm#i1010372

Many applications use session pooling to set up a number of sessions to be reused by multiple application users. Users authenticate themselves to a middle-tier application, which uses a single identity to log in to the database and maintains all the user connections. In this model, application users are users who are authenticated to the middle tier of an application, but who are not known to the database.....in these situations, the application typically connects as a single database user and all actions are taken as that user. Because all user sessions are created as the same user, this security model makes it very difficult to achieve data separation for each user. These applications can use the CLIENT_IDENTIFIER attribute to preserve the real application user identity through to the database.

从 Spring/JPA 方面看下面的 8.2 节:

http://docs.spring.io/spring-data/jdbc/docs/current/reference/html/orcl.connection.html

There are times when you want to prepare the database connection in certain ways that aren't easily supported using standard connection properties. One example would be to set certain session properties in the SYS_CONTEXT like MODULE or CLIENT_IDENTIFIER. This chapter explains how to use a ConnectionPreparer to accomplish this. The example will set the CLIENT_IDENTIFIER.

Spring 文档中给出的示例使用 XML 配置。如果您使用的是 Java 配置,那么它看起来像:

@Component
@Aspect
public class ClientIdentifierConnectionPreparer implements ConnectionPreparer
{
  @AfterReturning(pointcut = "execution(* *.getConnection(..))", returning = "connection")
  public Connection prepare(Connection connection) throws SQLException
  {
    String webAppUser = //from Spring Security Context or wherever;

    CallableStatement cs = connection.prepareCall(
                 "{ call DBMS_SESSION.SET_IDENTIFIER(?) }");
    cs.setString(1, webAppUser);
    cs.execute();
    cs.close();

    return connection;
  }
}

通过配置类启用 AspectJ:

@Configuration
@EnableAspectJAutoProxy
public class SomeConfigurationClass
{

}

请注意,虽然这隐藏在特定于 Spring 的 Oracle 扩展的部分中,但在我看来,第 8.2 节(与 8.1 不同)中没有任何内容是特定于 Oracle 的(执行的语句除外),一般方法应该是只需指定相关的过程调用或 SQL,即可适用于任何数据库:

例如 Postgres 如下,所以我不明白为什么使用 Postgres 的人不能在下面使用这种方法:

https://www.postgresql.org/docs/8.4/static/sql-set-role.html

关于java - 我如何使用 Hibernate/JPA 在插入/更新/删除之前告诉数据库用户是谁?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41897957/

相关文章:

java - 使用jeeunit时如何在每次测试后重置数据库?

java - 如何调用带有多个参数的spring API?

java - 警告 : Could not register destruction callback

java - Ajax 调用后重定向到一个页面,其中 URL 在 Spring Controller 中确定

sql - Oracle 连接运算符

SQL 与...更新

java - 在 Eclipse 的 Debug模式下动态执行命令/代码

java - 这个java阻塞队列变体可能吗?

java - 修改ArrayList的ArrayList时并发修改异常

sql - ORA-00903 : invalid table name