You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Spring Boot JPA查询中自定义数据库会话变量encrypt.key?

问题描述

我需要在数据库连接/查询时设置SET SESSION encrypt.key='some_key',模型类中的列定义如下:

@ColumnTransformer(forColumn = "first_name",
        read = "pgp_sym_decrypt(first_name, current_setting('encrypt.key'))",
        write = "pgp_sym_encrypt(?, current_setting('encrypt.key'))")
@Column(name = "first_name", columnDefinition = "bytea")
private String firstName;

直接在postgres.conf中设置encrypt.key时代码可正常运行,但现在需要从Spring配置文件中配置该密钥。我尝试过以下方法,但都存在问题:

  • 使用AttributeConverter注解和自定义转换器:仅适用于JPA操作,不支持LIKE查询。
  • 使用ContextEventListener在应用启动时执行SET SESSION语句:仅对部分请求生效。
  • 自定义继承JpaTransactionManager的CustomTransactionManager,重写prepareSynchronization方法:
@Override
protected void prepareSynchronization(DefaultTransactionStatus status,TransactionDefinition definition) {
        super.prepareSynchronization(status, definition);
        if (status.isNewTransaction()) {
            final String query = "SET encrypt.key='" + encryptKey + "'";
            entityManager.createNativeQuery(query).executeUpdate();
        }
        log.info("Encrypt Key : {}", entityManager.createNativeQuery("SElECT current_setting('encrypt.key')").getSingleResult());
    }
}

但调用普通JPA Repository方法时该类未被触发,encrypt.key设置失败。需要正确的实现方向指导。

可行解决方案

方案1:配置连接池初始化SQL(推荐)

如果使用HikariCP等主流连接池,可以直接配置连接初始化SQL,让每个从连接池获取的数据库连接自动执行密钥设置语句,覆盖所有数据库操作场景。

以HikariCP为例,在application.properties中添加:

spring.datasource.hikari.connection-init-sql=SET SESSION encrypt.key='${encrypt.key}'

或YAML格式:

spring:
  datasource:
    hikari:
      connection-init-sql: "SET SESSION encrypt.key='${encrypt.key}'"

这种方式无需修改代码,每个新创建的数据库连接都会预先执行该语句,会话级别的密钥配置自动生效。

方案2:使用Hibernate SessionEventListener绑定会话初始化

通过Hibernate的会话监听器,在每个Hibernate Session创建时执行密钥设置,确保所有基于该会话的JPA操作都能获取到密钥。

  1. 自定义会话监听器:
public class EncryptionKeySessionListener implements SessionEventListener {
    private final String encryptKey;

    public EncryptionKeySessionListener(String encryptKey) {
        this.encryptKey = encryptKey;
    }

    @Override
    public void sessionCreated(SessionCreatedEvent event) {
        event.getSession().doWork(connection -> {
            try (Statement statement = connection.createStatement()) {
                // 避免SQL注入,使用参数绑定
                String sql = "SET SESSION encrypt.key = ?";
                try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
                    pstmt.setString(1, encryptKey);
                    pstmt.execute();
                }
            }
        });
    }
}
  1. 注册监听器到Hibernate配置:
@Configuration
public class HibernateConfig {
    @Value("${encrypt.key}")
    private String encryptKey;

    @Bean
    public HibernatePropertiesCustomizer hibernatePropertiesCustomizer() {
        return properties -> {
            properties.put("hibernate.session_event_listeners", 
                          Collections.singletonList(new EncryptionKeySessionListener(encryptKey)));
        };
    }
}

方案3:修复自定义JpaTransactionManager的配置问题

之前的自定义事务管理器未被调用,是因为Spring容器没有使用该Bean。需要通过@Primary标记它为优先使用的事务管理器:

  1. 配置自定义事务管理器Bean:
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
    @Value("${encrypt.key}")
    private String encryptKey;

    @Autowired
    private EntityManagerFactory entityManagerFactory;

    @Primary
    @Bean
    public PlatformTransactionManager transactionManager() {
        CustomTransactionManager transactionManager = new CustomTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        transactionManager.setEncryptKey(encryptKey);
        return transactionManager;
    }
}
  1. 修改自定义事务管理器,修复SQL注入问题并正确获取EntityManager:
public class CustomTransactionManager extends JpaTransactionManager {
    private String encryptKey;

    public void setEncryptKey(String encryptKey) {
        this.encryptKey = encryptKey;
    }

    @Override
    protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
        super.prepareSynchronization(status, definition);
        if (status.isNewTransaction()) {
            EntityManager entityManager = getEntityManagerFactory().createEntityManager();
            try {
                // 使用参数绑定避免SQL注入
                entityManager.createNativeQuery("SET encrypt.key = ?1")
                             .setParameter(1, encryptKey)
                             .executeUpdate();
            } finally {
                entityManager.close();
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者Aditya T

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 18:35:41