我想知道Hibernate是否有办法执行ColumnTransformer的编程配置?

use*_*996 6 hibernate

我目前有一个这样的字段注释:

ColumnTransformer(
          read="AES_DECRYPT(C_first_name, 'yourkey')",
          write="AES_ENCRYPT(?, 'yourkey')")
public String getFirstName() {
   return firstName;
}
Run Code Online (Sandbox Code Playgroud)

这适用于Mysql数据库,但我需要这个配置是可选的,因为我们的应用程序可以使用另一个数据库(HsqlDB),具体取决于启动参数.所以我需要的是只有在使用特定的启动参数时才使用ColumnTransformer(并且没有用于HsqlDB的ColumnTransformer,它不能使用"AES_ENCRYPT")

有人可以帮我弄这个吗 ?

小智 5

我有同样的问题,我希望密钥可配置.我在这个项目中找到的唯一解决方案是在运行时更新注释值.是的,我知道这听起来很糟糕,但据我所知,没有别的办法.

实体类:

@Entity
@Table(name = "user")
public class User implements Serializable {
    @Column(name = "password")
    @ColumnTransformer(read = "AES_DECRYPT(password, '${encryption.key}')", write = "AES_ENCRYPT(?, '${encryption.key}')")
    private String password;
}
Run Code Online (Sandbox Code Playgroud)

我实现了将$ {encryption.key}替换为其他值的类(在我的情况下从Spring应用程序上下文加载)

import org.hibernate.annotations.ColumnTransformer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.Map;

import javax.annotation.PostConstruct;

@Component(value = "transformerColumnKeyLoader")
public class TransformerColumnKeyLoader {

    public static final String KEY_ANNOTATION_PROPERTY = "${encryption.key}"; 

    @Value(value = "${secret.key}")
    private String key;

    @PostConstruct
    public void postConstruct() {
        setKey(User.class, "password");
    }

    private void setKey(Class<?> clazz, String columnName) {
        try {
            Field field = clazz.getDeclaredField(columnName);

            ColumnTransformer columnTransformer = field.getDeclaredAnnotation(ColumnTransformer.class);
            updateAnnotationValue(columnTransformer, "read");
            updateAnnotationValue(columnTransformer, "write");
        } catch (NoSuchFieldException | SecurityException e) {
            throw new RuntimeException(
                    String.format("Encryption key cannot be loaded into %s,%s", clazz.getName(), columnName));
        }
    }

    @SuppressWarnings("unchecked")
    private void updateAnnotationValue(Annotation annotation, String annotationProperty) {
        Object handler = Proxy.getInvocationHandler(annotation);
        Field merberValuesField;
        try {
            merberValuesField = handler.getClass().getDeclaredField("memberValues");
        } catch (NoSuchFieldException | SecurityException e) {
            throw new IllegalStateException(e);
        }
        merberValuesField.setAccessible(true);
        Map<String, Object> memberValues;
        try {
            memberValues = (Map<String, Object>) merberValuesField.get(handler);
        } catch (IllegalArgumentException | IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
        Object oldValue = memberValues.get(annotationProperty);
        if (oldValue == null || oldValue.getClass() != String.class) {
            throw new IllegalArgumentException(String.format(
                    "Annotation value should be String. Current value is of type: %s", oldValue.getClass().getName()));
        }

        String oldValueString = oldValue.toString();
        if (!oldValueString.contains(TransformerColumnKeyLoader.KEY_ANNOTATION_PROPERTY)) {
            throw new IllegalArgumentException(
                    String.format("Annotation value should be contain %s. Current value is : %s",
                            TransformerColumnKeyLoader.KEY_ANNOTATION_PROPERTY, oldValueString));
        }
        String newValueString = oldValueString.replace(TransformerColumnKeyLoader.KEY_ANNOTATION_PROPERTY, key);

        memberValues.put(annotationProperty, newValueString);
    }
}
Run Code Online (Sandbox Code Playgroud)

创建EntityManager 之前运行此代码.在我的情况下,我使用依赖(对于xml配置或@DependsOn用于java配置).

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" depends-on="transformerColumnKeyLoader"> ... </bean>
Run Code Online (Sandbox Code Playgroud)

  • 如何为 Spring Boot 执行此操作 (4认同)
  • @Palsri 对于 Spring boot,不要将 TransformerColumnKeyLoader 设为 bean。只需在 SpringApplication.run() 之前将其实例化为 POJO,然后调用其 postConstruct() 即可。 (2认同)

ben*_*n75 1

hibernate 配置本质上是静态的。它不打算在运行时修改。但如果你认真做的话,是可以做到的。

基本上,构建 SessionFactory 的常用方法是执行以下操作:

  AnnotationConfiguration conf = new AnnotationConfiguration().configure();
  sessionFactory = conf.buildSessionFactory();
Run Code Online (Sandbox Code Playgroud)

大多数时候,此代码是框架的一部分(例如,使用 Spring,您必须查看 SessionFactoryBean 才能找到它)。因此,要做的第一件事就是识别这部分代码并覆盖执行此操作的框架组件,以便您可以在conf使用该对象之前访问该对象buildSessionFactory()

然后您必须修改 AnnotationConfiguration 以删除/添加与可选注释相关的数据:

  {
      ...
      AnnotationConfiguration conf = new AnnotationConfiguration().configure();
      if(FLAG_INDICATING_TO_REMOVE_SOME_ANNOTATION){
          manipulateHibernateConfig(conf);
      }
      sessionFactory = conf.buildSessionFactory();
      ...
  }

  private void manipulateHibernateConfig(AnnotationConfiguration conf){
      ...
     //this is the tricky part because lot of fields and setters are either
     //final or private so it requires reflection etc...

     //you must also be sure that those manipulation won't break the config !
  }
Run Code Online (Sandbox Code Playgroud)