在項目中有這個一個需求,查詢一個表,顯示爲datagrid,可是須要顯示的一列是個關聯表中的信息,是一對多的,可是須要把這個‘多’以逗號分隔顯示在這一列上。spring
舉例:sql
A1表 A2表數據庫
id name id A1_id namejson
1 name1 1 1 A2_name1ide
2 name2 2 1 A2_name2 this
3 2 A2_name3spa
查詢後獲得的顯示在頁面上的datagrid 應該爲:hibernate
id name A2_nameorm
1 name1 A2_name1, A2_name2 xml
2 name2 A2_name3
若是用hibernate你會怎麼作呢? 寫sql語句,一個sql能直接寫出這樣的結果的sql嗎? 我想了一下,很難,由於顯示結果須要改變 A2表的維度,須要將關聯的多條記錄轉化爲 一條記錄上的一列的值。有人說實話存儲過程吧, 若是用存儲過程了,那hibernate就失去了使用它的意義,在數據庫移植上就會有問題。咱們的原則是指作基本的sql處理,其餘的都是業務邏輯代替。有人說,用hibernate查詢A1表,而後獲得結果的List,而後遍歷這個List,拿到A1的id,關聯查詢A2表,或者A2的結果集,而後遍歷A2,將A2的那麼拼成字符串,而後構建新的Map對象,將A1的結果集的數據全都加入,而後將構建好的A2關聯信息的以逗號分隔的字符串,加入到Map的一個列中去。 嗯,這樣能夠實現咱們最終要的結果,可是,hibernate得到的結果集List,hibernate自己就須要循環jdbc的Resultset,而後將數據填充到實體對象或者Map對象當中去,而後組織成一個List對象。 而後你又須要再次遍歷這個List對象,講全部的數據取出來,關聯查詢,再從新組織成一個新的Map對象。 數據量小,列比較少,還能夠將就,可是數據量大,列大,又該如何? 研究了一上午hibernate的方法,好像就沒有這麼幹的。不知道hibernate是否原本就能夠支持。 若是hibernate自己就有辦法支持的話,請知道的朋友告知。 最後沒招,就研究了hibernate的Transformers 並讀了AliasedTupleSubsetResultTransformer的源代碼,因而有了點子。我重寫了AliasedTupleSubsetResultTransformer,代碼以下:
- public class XKAliasToBeanResultTransformer extends AliasedTupleSubsetResultTransformer
- {
- /**
- *
- */
- private static final long serialVersionUID = 5967847763983844234L;
- private final Class<?> resultClass;
- private boolean isInitialized;
- private String[] aliases;
- private Setter[] setters;
- public XKAliasToBeanResultTransformer(Class<?> resultClass)
- {
- if (resultClass == null)
- {
- throw new IllegalArgumentException("resultClass cannot be null");
- }
- isInitialized = false;
- this.resultClass = resultClass;
- }
- /**
- * {@inheritDoc}
- */
- public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength)
- {
- return false;
- }
- public Object transformTuple(Object[] tuple, String[] aliases)
- {
- Object result;
- if (!isInitialized)
- {
- initialize(aliases);
- }
- else
- {
- check(aliases);
- }
- // 關鍵代碼
- result = Initialization.getInstance().getApplicationContext().getBean(resultClass);
- for (int i = 0; i < aliases.length; i++)
- {
- if (setters[i] != null)
- {
- setters[i].set(result, tuple[i], null);
- }
- }
- return result;
- }
- private void initialize(String[] aliases)
- {
- PropertyAccessor propertyAccessor = new ChainedPropertyAccessor(new PropertyAccessor[] { PropertyAccessorFactory.getPropertyAccessor(resultClass, null),
- PropertyAccessorFactory.getPropertyAccessor("field") });
- this.aliases = new String[aliases.length];
- setters = new Setter[aliases.length];
- for (int i = 0; i < aliases.length; i++)
- {
- String alias = aliases[i];
- if (alias != null)
- {
- this.aliases[i] = alias;
- setters[i] = propertyAccessor.getSetter(resultClass, alias);
- }
- }
- isInitialized = true;
- }
- private void check(String[] aliases)
- {
- if (!Arrays.equals(aliases, this.aliases))
- {
- throw new IllegalStateException("aliases are different from what is cached; aliases=" + Arrays.asList(aliases) + " cached=" + Arrays.asList(this.aliases));
- }
- }
- public boolean equals(Object o)
- {
- if (this == o)
- {
- return true;
- }
- if (o == null || getClass() != o.getClass())
- {
- return false;
- }
- XKAliasToBeanResultTransformer that = (XKAliasToBeanResultTransformer) o;
- if (!resultClass.equals(that.resultClass))
- {
- return false;
- }
- if (!Arrays.equals(aliases, that.aliases))
- {
- return false;
- }
- return true;
- }
- public int hashCode()
- {
- int result = resultClass.hashCode();
- result = 31 * result + (aliases != null ? Arrays.hashCode(aliases) : 0);
- return result;
- }
- }
上面「關鍵代碼」的地方,原來的代碼,只是resultClass.newInstance(); 個人修改其實很簡單,就是使用spring的Context獲取的bean而已,這樣bean就能夠被加強了,能夠注入,能夠支持事務了。因而,咱們就能夠在hibernate像實體對象填充數據的時機作任何的事情,包括我上面的需求,一旦hibernate像實體對象填充數據,就要調用set方法,在調用setId這個方法的時候,咱們就能得到每條記錄的id,而後能夠查詢關聯表,組織處以逗號分隔的關聯表多條記錄的名稱,而後填充到實體對象的某一個屬性上。這樣,查詢完畢,咱們得到的List中的實體對象,就已經包含了咱們要的多表合併出的那列的值,咱們只用json一下,返回前臺,輕鬆的就能夠在datagrid上顯示出咱們要的結果。
這樣作的壞處,目前看只有一條,就是沒法應對hibernate的升級,若是升級變更大,就須要修改調整代碼了。
若是有朋友有更好的辦法,能夠提出交流,個人這種辦法,有點簡單粗暴,可是能夠應對一切不可能完成的任務。 其實說白了,至關於在hibernate給實體對象填充數據的過程當中攔截。