当存在将多个列组合为行键的复合主键时,覆盖/实现getRowKey()和getRowData()方法

Tin*_*iny 5 datatable jsf composite-key primefaces lazydatamodel

我在MySQL数据库中有一个表.不幸的是,GlassFish Server中的JAAS身份验证/授权需要一个复合主键.

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)
Run Code Online (Sandbox Code Playgroud)

该表包含以下格式的数据.

mysql> select * from group_table;
+-------------------------+------------+
| user_group_id           | group_id   |
+-------------------------+------------+
| you123@gmail.com        | ROLE_ADMIN |
| you123@gmail.com        | ROLE_USER  |
| you123@ymail.com        | ROLE_USER  |
| you123@hotmail.com      | ROLE_USER  |
| you123@yahoo.com        | ROLE_USER  |
+-------------------------+------------+
5 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

一个<p:dataTable>rowKey工作正常,当lazy设置为false.

<p:dataTable rowKey="#{row.groupTablePK.userGroupId} #{row.groupTablePK.groupId}">
    ...
</p:dataTable>
Run Code Online (Sandbox Code Playgroud)

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.
    }
}
Run Code Online (Sandbox Code Playgroud)

上述实现不完整.

<p:dataTable>使用这些实现中选择行时,方法sout内的语句将getRowData()显示以下内容.

Info:  rowKey : entity.GroupTablePK[ userGroupId=you123@gmail.com
Info:  rowKey : groupId=ROLE_USER ]
Run Code Online (Sandbox Code Playgroud)

getRowKey()方法返回一个实例,GroupTablePK但该getRowData()方法只接受String类型参数.它不是表示复合主键(在此GroupTablePK)的对象,因此它可以被类型转换为适当的对象类型(GroupTablePK),并且基于该对象GroupTable可以从给定的实例中获取List<GroupTable>并获取该getRowData()方法以返回该实例GroupTable.

怎么进一步?


问题完全基于前一个问题:

java.lang.UnsupportedOperationException:当不使用基本rowKey算法时,必须实现getRowData(String rowKey)


编辑:

hashcode()equals()除了实现toString()GroupTablePK.的toString()在方法GroupTablePK返回return "entity.GroupTablePK[ userGroupId=" + userGroupId + ", groupId=" + groupId + " ]";,但getRowData()方法被调用两次,当在一排<p:dataTable>被选择.它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;
}
Run Code Online (Sandbox Code Playgroud)

编辑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());
    }
}
Run Code Online (Sandbox Code Playgroud)

数据表:

<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>
Run Code Online (Sandbox Code Playgroud)

尝试编辑行时,将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 + " ]";
    }
}
Run Code Online (Sandbox Code Playgroud)
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 + " ]";
    }
}
Run Code Online (Sandbox Code Playgroud)

Bal*_*usC 6

我跑了你的MCVE(对此赞不绝口!)并重现了它.rowkey似乎被解释为客户端中的逗号分隔字符串,以涵盖需要多个选择时的情况.如果单个rowkey的字符串表示形式包含逗号,则会失败,如您的情况.您获得的rowkey参数getRowData()是明确的证据:它们是原始值在逗号上拆分时的结果.

因此,要解决此问题,您需要确保在getRowKey().toString()任何地方都不包含逗号.更好地使用不同的分隔符.例如下划线.

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

  • @maple_shaft:希望你喜欢这样一个事实:OmniFaces试图通过抛出IllegalArgumentException来预测并覆盖这个事实,当开发人员犯了错误时,所有地方都有一条明确的消息错误;) (2认同)