2015-04-26 52 views
3

我在MySQL數據庫中有一個表。不幸的是,在GlassFish服務器中存在JAAS認證/授權所需的組合主鍵。當存在將多個列組合爲主鍵的複合主鍵時,覆蓋/實現getRowKey()和getRowData()方法

mysql> desc group_table; 
+---------------+--------------+------+-----+---------+-------+ 
| Field   | Type   | Null | Key | Default | Extra | 
+---------------+--------------+------+-----+---------+-------+ 
| user_group_id | varchar(176) | NO | PRI | NULL |  | 
| group_id  | varchar(15) | NO | PRI | NULL |  | 
+---------------+--------------+------+-----+---------+-------+ 
2 rows in set (0.05 sec) 

該表格包含以下格式的數據。

mysql> select * from group_table; 
+-------------------------+------------+ 
| user_group_id   | group_id | 
+-------------------------+------------+ 
| [email protected]  | ROLE_ADMIN | 
| [email protected]  | ROLE_USER | 
| [email protected]  | ROLE_USER | 
| [email protected]  | ROLE_USER | 
| [email protected]  | ROLE_USER | 
+-------------------------+------------+ 
5 rows in set (0.00 sec) 

一個<p:dataTable>rowKey工作正常,當lazy設置爲false

<p:dataTable rowKey="#{row.groupTablePK.userGroupId} #{row.groupTablePK.groupId}"> 
    ... 
</p:dataTable> 

GroupTablePK@Embeddable類(JPA)。我認爲這個課程的細節是不需要的。


lazy然而,能在<p:dataTable>getRowKey()getRowData()方法需要實現。

如何做到這一點,當有一個組合主鍵需要組合的列作爲行鍵 - 唯一的行標識符?

@Named 
@ViewScoped 
public class UserAuthorityManagedBean extends LazyDataModel<GroupTable> implements Serializable { 

    private static final long serialVersionUID = 1L; 

    @Override 
    public Object getRowKey(GroupTable groupTable) { 
     return groupTable != null ? groupTable.getGroupTablePK() : null; 
    } 

    @Override 
    public GroupTable getRowData(String rowKey) { 
     List<GroupTable> list = (List<GroupTable>) getWrappedData(); 

     System.out.println("rowKey : " + rowKey); 
    } 

    @Override 
    public List<GroupTable> load(int first, int pageSize, List<SortMeta> multiSortMeta, Map<String, Object> filters) { 
     //... setRowCount(rowCount); 
     //... Return a List<GroupTable> from a business Service. 
    } 
} 

上述實現不完整。

使用這些實現在<p:dataTable>中選擇一行時,getRowData()方法中的sout語句顯示以下內容。

Info: rowKey : entity.GroupTablePK[ [email protected] 
Info: rowKey : groupId=ROLE_USER ] 

getRowKey()方法返回的GroupTablePK一個實例,但getRowData()方法只接受一個String類型參數。它不是表示組合主鍵的對象(在此爲GroupTablePK),以便可以將其類型轉換爲適當的對象類型(GroupTablePK),並且基於該對象可以從給定的List<GroupTable>獲得GroupTable的實例並獲得getRowData()方法返回GroupTable的實例。

如何繼續?


問題是純粹基於即刻前一個問題:

java.lang.UnsupportedOperationException: getRowData(String rowKey) must be implemented when basic rowKey algorithm is not used


編輯:

我除了toString()在和equals()實現10。在GroupTablePK中的toString()方法返回return "entity.GroupTablePK[ userGroupId=" + userGroupId + ", groupId=" + groupId + " ]";,但當選擇<p:dataTable>中的行時,將調用getRowData()方法兩次。它在兩個後續調用中以兩部分的形式返回GroupTablePK的字符串表示形式。在第一次通話中,它返回entity.GroupTablePK[ userGroupId=aaa,然後在第二次通話中返回groupId=ROLE_USER ]

它應該在一次調用中立即返回entity.GroupTablePK[ userGroupId=aaa, groupId=ROLE_USER ]

這種比較groupTable.getGroupTablePK().toString().equals(rowKey)因此是不可能的,這是我在本文之前想到的。如,

@Override 
public GroupTable getRowData(String rowKey) { 
    List<GroupTable> list = (List<GroupTable>) getWrappedData(); 

    for (GroupTable groupTable : list) { 
     if (groupTable.getGroupTablePK().toString().equals(rowKey)) { 
      return groupTable; 
     } 
    } 

    return null; 
} 

編輯2:

以下是最短的例子去除JPA噪聲重現該問題。

試圖替代地上,

  • PrimeFaces 3.5
  • PrimeFaces 4.0
  • PrimeFaces 5.0
  • PrimeFaces 5.1
  • PrimeFaces 5.2

行爲保持靜止在所有的這些PrimeFaces版本。

託管bean:

@Named 
@ViewScoped 
public class CompositeRowKeyManagedBean extends LazyDataModel<GroupTable> implements Serializable { 

    private List<GroupTable> selectedValues; // Getter & setter. 
    private static final long serialVersionUID = 1L; 

    public CompositeRowKeyManagedBean() {} 

    private List<GroupTable> init() { 
     List<GroupTable> list = new ArrayList<GroupTable>(); 

     GroupTablePK groupTablePK = new GroupTablePK("aaa", "ROLE_ADMIN"); 
     GroupTable groupTable = new GroupTable(groupTablePK); 
     list.add(groupTable); 

     groupTablePK = new GroupTablePK("bbb", "ROLE_USER"); 
     groupTable = new GroupTable(groupTablePK); 
     list.add(groupTable); 

     groupTablePK = new GroupTablePK("ccc", "ROLE_USER"); 
     groupTable = new GroupTable(groupTablePK); 
     list.add(groupTable); 

     groupTablePK = new GroupTablePK("ddd", "ROLE_USER"); 
     groupTable = new GroupTable(groupTablePK); 
     list.add(groupTable); 

     groupTablePK = new GroupTablePK("eee", "ROLE_USER"); 
     groupTable = new GroupTable(groupTablePK); 
     list.add(groupTable); 
     return list; 
    } 

    @Override 
    public List<GroupTable> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) { 
     List<GroupTable> list = init(); 
     setRowCount(list.size()); 
     return list; 
    } 

    @Override 
    public Object getRowKey(GroupTable groupTable) { 
     return groupTable != null ? groupTable.getGroupTablePK() : null; 
    } 

    @Override 
    public GroupTable getRowData(String rowKey) { 
     List<GroupTable> list = (List<GroupTable>) getWrappedData(); 
     System.out.println("rowKey : " + rowKey); 

     for (GroupTable groupTable : list) { 
      if (groupTable.getGroupTablePK().toString().equals(rowKey)) { 
       return groupTable; 
      } 
     } 

     return null; 
    } 

    public void onRowEdit(RowEditEvent event) { 
     GroupTablePK groupTablePK = ((GroupTable) event.getObject()).getGroupTablePK(); 
     System.out.println("grouoId : " + groupTablePK.getGroupId() + " : userGroupId : " + groupTablePK.getUserGroupId()); 
    } 
} 

數據表:

<p:dataTable var="row" 
      value="#{compositeRowKeyManagedBean}" 
      lazy="true" 
      editable="true" 
      selection="#{compositeRowKeyManagedBean.selectedValues}" 
      rows="50"> 
    <p:column selectionMode="multiple"></p:column> 

    <p:ajax event="rowEdit" listener="#{compositeRowKeyManagedBean.onRowEdit}"/> 

    <p:column headerText="GroupId"> 
     <h:outputText value="#{row.groupTablePK.userGroupId}"/> 
    </p:column> 

    <p:column headerText="UserGroupId"> 
     <p:cellEditor> 
      <f:facet name="output"> 
       <h:outputText value="#{row.groupTablePK.groupId}"/> 
      </f:facet> 
      <f:facet name="input"> 
       <p:inputText value="#{row.groupTablePK.groupId}"/> 
      </f:facet> 
     </p:cellEditor> 
    </p:column> 

    <p:column headerText="Edit"> 
     <p:rowEditor/> 
    </p:column> 
</p:dataTable> 

當行試圖進行編輯,該方法onRowEdit()被調用。如前所述,getRowData()被調用兩次,並在後續兩次調用中產生行鍵的分割。


這是兩個領域類GroupTableGroupTablePK

public class GroupTable implements Serializable { 

    private static final long serialVersionUID = 1L; 
    protected GroupTablePK groupTablePK; 

    public GroupTable() {} 

    public GroupTable(GroupTablePK groupTablePK) { 
     this.groupTablePK = groupTablePK; 
    } 

    public GroupTable(String userGroupId, String groupId) { 
     this.groupTablePK = new GroupTablePK(userGroupId, groupId); 
    } 

    public GroupTablePK getGroupTablePK() { 
     return groupTablePK; 
    } 

    public void setGroupTablePK(GroupTablePK groupTablePK) { 
     this.groupTablePK = groupTablePK; 
    } 

    @Override 
    public int hashCode() { 
     int hash = 0; 
     hash += (groupTablePK != null ? groupTablePK.hashCode() : 0); 
     return hash; 
    } 

    @Override 
    public boolean equals(Object object) { 
     if (!(object instanceof GroupTable)) { 
      return false; 
     } 
     GroupTable other = (GroupTable) object; 
     if ((this.groupTablePK == null && other.groupTablePK != null) || (this.groupTablePK != null && !this.groupTablePK.equals(other.groupTablePK))) { 
      return false; 
     } 
     return true; 
    } 

    @Override 
    public String toString() { 
     return "entity.GroupTable[ groupTablePK=" + groupTablePK + " ]"; 
    } 
} 
public class GroupTablePK implements Serializable { 

    private String userGroupId; 
    private String groupId; 

    public GroupTablePK() {} 

    public GroupTablePK(String userGroupId, String groupId) { 
     this.userGroupId = userGroupId; 
     this.groupId = groupId; 
    } 

    public String getUserGroupId() { 
     return userGroupId; 
    } 

    public void setUserGroupId(String userGroupId) { 
     this.userGroupId = userGroupId; 
    } 

    public String getGroupId() { 
     return groupId; 
    } 

    public void setGroupId(String groupId) { 
     this.groupId = groupId; 
    } 

    @Override 
    public int hashCode() { 
     int hash = 0; 
     hash += (userGroupId != null ? userGroupId.hashCode() : 0); 
     hash += (groupId != null ? groupId.hashCode() : 0); 
     return hash; 
    } 

    @Override 
    public boolean equals(Object object) { 
     if (!(object instanceof GroupTablePK)) { 
      return false; 
     } 
     GroupTablePK other = (GroupTablePK) object; 
     if ((this.userGroupId == null && other.userGroupId != null) || (this.userGroupId != null && !this.userGroupId.equals(other.userGroupId))) { 
      return false; 
     } 
     if ((this.groupId == null && other.groupId != null) || (this.groupId != null && !this.groupId.equals(other.groupId))) { 
      return false; 
     } 
     return true; 
    } 

    @Override 
    public String toString() { 
     return "entity.GroupTablePK[ userGroupId=" + userGroupId + ", groupId=" + groupId + " ]"; 
    } 
} 

回答

3

我跑了你的MCVE(榮譽!),並轉載它。 rowkey似乎被解釋爲客戶端中的逗號分隔字符串,以便在需要多個選擇時覆蓋該情況。如果單個rowkey的字符串表示包含一個逗號,這將失敗,就像你的情況一樣。您在getRowData()中獲得的rowkey參數清楚地表明瞭它的作用:它們是原始值在逗號分隔時的結果。

因此,要解決此問題,您需要確保getRowKey().toString()的任何位置都不包含逗號。更好地使用不同的分隔符。例如。下劃線。

@Override 
public Object getRowKey(GroupTable groupTable) { 
    GroupTablePK pk = groupTable != null ? groupTable.getGroupTablePK() : null; 
    return pk != null ? pk.getUserGroupId() + "_" + pk.getGroupId() : null; 
} 
+0

哇...很好找。這是我停止使用Primefaces的原因之一,因爲這些沒有文檔的細微差別。 –

+2

@maple_shaft:希望你喜歡這樣一個事實,即OmniFaces試圖通過拋出IllegalArgumentException來預測和覆蓋這個事實,並在開發人員將它錯誤的情況下清除所有位置的消息;) – BalusC

1

從閱讀你的問題我猜測的getRowKey()方法必須返回的東西是唯一可識別的單行。可以理解的是,代表你的行的底層JPA實體有一個很好的組合鍵對象。我認爲這是Java對象在Map類型集合中使用任何東西作爲鍵的問題,關鍵對象必須重載併爲equals和方法定義適當的實現。

我懷疑Primefaces可能正在使用某種Map來根據一個鍵檢索值。字符串類型通常是對象的唯一鍵的一個很好的候選者,因爲字符串是不可變的,並且具有正確的equals和hashCode實現。他們是一個很好的候選人,所以如果你必須傳遞一個字符串到getRowData那麼你總是可以在該對象上提供一個方法來返回該對象的唯一字符串。例如,這可能是您爲行數據對象提供的hashCode實現的基本表示形式。

如果String不是必需的參數,那麼簡單地爲複合鍵對象實現equals和hashCode,並直接使用它作爲你的鍵。

+0

當選中某行時,會調用'getRowData()'方法兩次。第一次,它產生'entity.GroupTablePK [userGroupId = ddddddddddddddddddddddd',然後'groupId = ROLE_USER]' - 'sout'語句產生的日誌'INFO'。因此,在這種情況下,'toString()'實現在某些方面似乎也不明智。 – Tiny

+0

可以多次調用getRowData。如果您的toString()實現對於給定的對象是唯一的,那麼它應該是一個完美的關鍵。如果沒有,那麼你的'getWrappedData'方法可能會出現錯誤。你能編輯你的問題並提供該代碼嗎? –

+0

['getWrappedData()'](http://www.primefaces.org/docs/api/5.2/org/primefaces/model/LazyDataModel.html#getWrappedData())方法屬於['LazyDataModel ']( http://www.primefaces.org/docs/api/5.2/org/primefaces/model/LazyDataModel.html)。這是所討論的託管bean擴展的超類。它自己從一個延遲加載的''中返回行。上下文無關。 – Tiny