2016-11-17 115 views
2

我在我的實體模型中有以下字段。如何忽略單元測試中的某些字段,休眠

@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來從某些數據庫寫忽略這個字段。或
  2. 如何獲得HSQL和/或H2與這個mysql函數一起工作。

謝謝

+0

對於第一種解決方案,您爲什麼要忽略此字段的書寫?你不能只在斷言中忽略這個字段?你可以在你的問題中提供更多關於你想要測試的細節。 – davidxxx

+0

@davidxxx我想忽略該字段,因爲當hibernate嘗試插入表時,由於函數不兼容(HSQL中基本不存在),它會拋出異常。我用堆棧跟蹤更新了問題 –

回答

0

你有你的實體類和你的DBMS的功能之間的強耦合。
在這裏,最大的問題是耦合是在元數據上,您不能在運行時輕鬆禁用。因此,爲了編寫一些單元測試,它會鼓勵你在運行時使用字節碼修改來獲得你想要的。但是它會給你的單元測試帶來複雜性和潛在的副作用。
此外,代碼應儘可能自然地測試(只是一個評論:使用TDD方法,這種問題可能會被迅速注意到並處理)。因此,如果你可以修改代碼,我認爲一個更好的想法是將加密/解密問題與領域問題分開,你可以選擇你想要加密/解密的實現方式。
通過這種方式,在應用範圍內,您可以使用真正的實現並在單元測試中使用 ,您可以使用不做任何事情或甚至根本沒有實現的假實現。
Hibernate提供實體監聽器機制。有了它們,您可以在實體生命週期的某個時間掛鉤。

這裏你可以找到更多信息: https://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/listeners.h TML

這些回調可能會感興趣:

@PrePersist Executed before the entity manager persist operation is actually executed or cascaded. This call is synchronous with the persist operation.

加密值。

@PostLoad Executed after an entity has been loaded into the current persistence context or an entity has been refreshed.

解密值。

最後,您可以爲hibernate配置兩個配置文件,一個配置監聽器,另一個配置文件不需要監聽器進行單元測試。

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); 
    } 
} 
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;