如何在单元测试中忽略某些字段,Hibernate

时间:2016-11-17 19:34:20

标签: java mysql hibernate hsqldb

我的实体模型中有以下字段。

@Column(name="key")
@ColumnTransformer(
        read="AES_DECRYPT(key, SHA1('passcode'))", 
        write="AES_ENCRYPT(?, SHA1('passcode'))")
private String secret_key;

我正在使用MySql数据库,而hibernate将使用AES_ENCRYPT和AES_DECRYPT函数完美地加密/解密密钥值。但是,spring嵌入式数据库(HSQL或H2)不知道这个特定的MySql函数。它会引发以下错误:

 javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not insert: [com.xxx.Table]
    at ...

Caused by: org.hibernate.exception.SQLGrammarException: could not insert: [com.xxx.Table]
    at ...

Caused by: java.sql.SQLSyntaxErrorException: user lacks privilege or object not found: AES_ENCRYPT

所以我的问题是,

  1. 如何告诉hibernate在某些数据库中写入时忽略该字段。 OR
  2. 如何使用此mysql函数获得HSQL和/或H2。
  3. 谢谢

3 个答案:

答案 0 :(得分:1)

将此类添加到测试配置中:

@Component
public class RemoveAesFunction {



    @PostConstruct
    public void postConstruct() {
        setKey(MyEntity.class);
    }

    private void setKey(Class<?> clazz) {
        try {
            Field field = clazz.getDeclaredField("firstName");

            ColumnTransformer columnTransformer = field.getDeclaredAnnotation(ColumnTransformer.class);
            updateAnnotationValue(columnTransformer, "read","");
            updateAnnotationValue(columnTransformer, "write","?");
        } catch (NoSuchFieldException | SecurityException e) {
            throw new RuntimeException();
        }
    }

    @SuppressWarnings("unchecked")
    private void updateAnnotationValue(Annotation annotation, String annotationProperty,String value) {
        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);
        }

        memberValues.put(annotationProperty, value);
    }
}

答案 1 :(得分:0)

您的实体类和dbms函数之间存在强大的耦合 在这里,最大的问题是耦合是在运行时无法轻易禁用的元数据上。因此,要编写一些单元测试,它将鼓励您在运行时使用字节码修改来拥有您想要的。但它会给您的单元测试带来复杂性和潜在的副作用 此外,代码应该尽可能自然地测试(只是一句话:使用TDD方法,这种问题可能会被快速注意和处理)。

因此,如果您可以修改代码,我认为更好的想法是将加密/解密问题与域事项分开,以便您可以选择加密/解密问题的实现。
这样,在应用范围中,您可以使用真正的实现和 在单元测试中,你可以使用一个虚假的实现,它什么都不做,甚至没有更好的实现 Hibernate提供了实体监听器机制。有了它们,你可以在实体生命周期的某个时候有钩子。

在这里您可以找到更多信息: https://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/listeners.h TML

这些回调可能让您感兴趣:

  

@PrePersist在实体管理器持久运行之前执行   实际执行或级联。此调用与   坚持不懈。

加密价值。

  

@PostLoad将实体加载到当前后执行   持久化上下文或实体已刷新。

解密价值。

最后,你可以有两个用于hibernate的配置文件,一个用于监听器,另一个用于没有用于单元测试的监听器。

答案 2 :(得分:0)

使用HSQLDB,您可以创建这些功能以进行测试。

CREATE FUNCTION AES_DECRYPT(VAL VARCHAR(32000), KEY VARCHAR(32000)) RETURNS VARCHAR(32000) RETURN VAL;

CREATE FUNCTION AES_ENCRYPT(VAL VARCHAR(32000), KEY VARCHAR(32000)) RETURNS VARCHAR(32000) RETURN VAL;