为什么多次调用JSF转换器的getAsObject()方法?

时间:2013-12-02 20:38:26

标签: jpa jsf-2 entity converter

我的数据库结构通过连接表将用户实体耦合到角色,其中User类拥有该关系并拥有一个引用角色实体集合的字段。

我想使用SelectOneMenu将User链接到相关角色,但这当然会返回Role对象而不是Collection。虽然SelectManyMenu会通过返回Collection来绕过此问题,但只应为Users分配一个Role。我正在使用Omnifaces SelectItemsConverter类来处理Object<> String转换,并认为我可以覆盖getAsObject以返回包含单个选定Role的Collection。但是,这不起作用 - 调试表单的处理显示在JSF验证过程中,getAsObject方法被调用三次(有三个关联的SQL调用查询Roles表)。第一次传递选定的String值,但接下来的两个调用包含一个空String但是尽管如此,Role setter只被调用一次(在更新模型阶段)并且接收到正确的值。

我通过在代码处理集合中将用户实体的持久性添加到数据库中的角色实体来解决这个问题,但这似乎并不优雅。如果可能的话,我宁愿在一个地方保持转换为User实体所需的对象。更重要的是,我不明白为什么我会看到这种行为,无论是正常还是错误代码的结果以及多个数据库查询在生产场景中可能产生的影响。不知道是在困扰我(没有双关语!),因为它表明我无法理解JSF生命周期和servlet处理的细节。

此方法填充包含角色实体的SelectOneMenu:

public static SelectItem[] getSelectItems(List<?> entities, boolean selectOne) {
    int size = selectOne ? entities.size() + 1 : entities.size();
    SelectItem[] items = new SelectItem[size];
    int i = 0;
    if (selectOne) {
        items[0] = new SelectItem("", "Please select one item");
        i++;
    }
    for (Object x : entities) {
        items[i++] = new SelectItem(x, x.toString());
    }
    return items;
}

RolesSelectItemConverter.getAsObjec()方法与标准Omnifaces实现相同,但调用方法时调用Logger标记除外。

调试跟踪:

INFO:   START PHASE PROCESS_VALIDATIONS 3
INFO:   RoleSelectItemsConverter.getAsObject called for UOComponent {javax.faces.component.html.HtmlSelectOneMenu@368dde0b} and value {exhibitor}
INFO:   Attempting find all: com.rms.myconferenceprofile.entity.Role
INFO:   [EL Fine]: sql: 2013-12-02 19:59:50.036--ServerSession(1830578621)--Connection(2011154278)--Thread(Thread[http-listener-1(1),5,main])--SELECT ID, DESCRIPTION, ROLE_NAME FROM ROLES
INFO:   Attempting find all: com.rms.myconferenceprofile.entity.Role
INFO:   [EL Fine]: sql: 2013-12-02 19:59:50.039--ServerSession(1830578621)--Connection(1600432374)--Thread(Thread[http-listener-1(1),5,main])--SELECT ID, DESCRIPTION, ROLE_NAME FROM ROLES
INFO:   RoleSelectItemsConverter.getAsObject called for UOComponent {javax.faces.component.html.HtmlSelectOneMenu@368dde0b} and value {}
INFO:   Attempting find all: com.rms.myconferenceprofile.entity.Role
INFO:   [EL Fine]: sql: 2013-12-02 19:59:50.042--ServerSession(1830578621)--Connection(947366464)--Thread(Thread[http-listener-1(1),5,main])--SELECT ID, DESCRIPTION, ROLE_NAME FROM ROLES
INFO:   [EL Fine]: sql: 2013-12-02 19:59:50.044--ServerSession(1830578621)--Connection(966514315)--Thread(Thread[http-listener-1(1),5,main])--SELECT t1.ID, t1.EMAIL, t1.FORENAME, t1.INACTIVE, t1.PASSWORD, t1.PHONE_NUMBER, t1.REGISTERED_DATETIME, t1.SURNAME, t1.ADDRESS_ID FROM USER_ROLE_MAP t0, USERS t1 WHERE ((t0.ROLE_ID = ?) AND (t1.ID = t0.USER_ID))
    bind => [2]
INFO:   Attempting find all: com.rms.myconferenceprofile.entity.Role
INFO:   [EL Fine]: sql: 2013-12-02 19:59:50.048--ServerSession(1830578621)--Connection(119432927)--Thread(Thread[http-listener-1(1),5,main])--SELECT ID, DESCRIPTION, ROLE_NAME FROM ROLES
INFO:   RoleSelectItemsConverter.getAsObject called for UOComponent {javax.faces.component.html.HtmlSelectOneMenu@368dde0b} and value {}
INFO:   Attempting find all: com.rms.myconferenceprofile.entity.Role
INFO:   [EL Fine]: sql: 2013-12-02 19:59:50.051--ServerSession(1830578621)--Connection(1342595455)--Thread(Thread[http-listener-1(1),5,main])--SELECT ID, DESCRIPTION, ROLE_NAME FROM ROLES
INFO:   END PHASE PROCESS_VALIDATIONS 3
INFO:   START PHASE UPDATE_MODEL_VALUES 4
INFO:   SelectedRole setter called. Value: exhibitor
INFO:   END PHASE UPDATE_MODEL_VALUES 4

我刚刚获得了一些调试信息,看看有多少次调用Role.equals()以及比较对象是什么。这有趣的阅读!我可以理解通过可用角色实体的第一组迭代(当识别出用户选择的角色时迭代停止)但是通过整个角色表的第二组迭代让我感到困惑。如果看一个大表(例如国家或州),我的代码看起来效率很低!

INFO:   START PHASE PROCESS_VALIDATIONS 3
INFO:   RoleSelectItemsConverter.getAsObject called for UOComponent {javax.faces.component.html.HtmlSelectOneMenu@18a45031} and value {eventOrg}
INFO:   Attempting find all: com.rms.myconferenceprofile.entity.Role
INFO:   [EL Fine]: sql: 2013-12-02 20:54:37.596--ServerSession(1165546789)--Connection(544502930)--Thread(Thread[http-listener-1(3),5,main])--SELECT ID, DESCRIPTION, ROLE_NAME FROM ROLES
INFO:   Attempting find all: com.rms.myconferenceprofile.entity.Role
INFO:   [EL Fine]: sql: 2013-12-02 20:54:37.599--ServerSession(1165546789)--Connection(1032878258)--Thread(Thread[http-listener-1(3),5,main])--SELECT ID, DESCRIPTION, ROLE_NAME FROM ROLES
INFO:   RoleSelectItemsConverter.getAsObject called for UOComponent {javax.faces.component.html.HtmlSelectOneMenu@18a45031} and value {}
INFO:   Attempting find all: com.rms.myconferenceprofile.entity.Role
INFO:   [EL Fine]: sql: 2013-12-02 20:54:47.789--ServerSession(1165546789)--Connection(948440937)--Thread(Thread[http-listener-1(3),5,main])--SELECT ID, DESCRIPTION, ROLE_NAME FROM ROLES
INFO:   equals() called - comparison object {}
INFO:   equals() called - comparison object {delegate}
INFO:   equals() called - comparison object {exhibitor}
INFO:   Attempting find all: com.rms.myconferenceprofile.entity.Role
INFO:   equals() called - comparison object {eventOrg}
INFO:   [EL Fine]: sql: 2013-12-02 20:54:47.793--ServerSession(1165546789)--Connection(1537445637)--Thread(Thread[http-listener-1(3),5,main])--SELECT ID, DESCRIPTION, ROLE_NAME FROM ROLES
INFO:   RoleSelectItemsConverter.getAsObject called for UOComponent {javax.faces.component.html.HtmlSelectOneMenu@18a45031} and value {}
INFO:   Attempting find all: com.rms.myconferenceprofile.entity.Role
INFO:   [EL Fine]: sql: 2013-12-02 20:55:08.661--ServerSession(1165546789)--Connection(697639746)--Thread(Thread[http-listener-1(3),5,main])--SELECT ID, DESCRIPTION, ROLE_NAME FROM ROLES
INFO:   equals() called - comparison object {}
INFO:   equals() called - comparison object {delegate}
INFO:   equals() called - comparison object {exhibitor}
INFO:   equals() called - comparison object {eventOrg}
INFO:   equals() called - comparison object {venueOrg}
INFO:   equals() called - comparison object {ADMIN}
INFO:   END PHASE PROCESS_VALIDATIONS 3

Xtreme Biker协助后的其他信息:

我没有使用JSF Managed Beans,因为据我所知,支持将在下一个版本发布后撤销。 getSelectItems方法包含在未明确作用域的实用程序类中 - 因为我正在使用CDI,我想唯一与@ViewScope等效的范围是@ConversationScope?此方法取消了NetBeans自动生成的代码,因此可能不是最佳的。

1 个答案:

答案 0 :(得分:0)

您提供的代码显示您在不适当的位置执行持久层访问。假设您的托管bean是@ViewScoped,那么应该使用preRenderView事件或@PostConstruct来调用持久层,将所有加载的值保留在变量中。稍后,您将在getter方法中返回这些变量。在最终用户执行另一个请求之前,无需再次访问数据库。

因此,在getter方法中执行此类操作被认为是一种不好的做法。每次需要访问属性时,JSF都会调用该方法,因为它不会缓存这些值(您需要在自己的bean中执行此操作,并将其存储在变量中)。

同样在Converter类中命中数据库被认为是坏的,但是应该使用Omnifaces转换器来避免它。

您似乎使用getSelectItems方法填充可用值。但是,这不是您从视图中调用的内容,因为它有两个参数。你用什么方法来称呼它?你一次又一次地加载List<?> entities吗?

与此问题无关我建议您使用Java名称约定。以get开头的方法是getters,并且它们不会收到任何参数。 setter个只接收一个参数,与他们必须设置的参数相对应。任何不同的方法都不应以getset开头(您可以使用loadprepare前缀。)

另见: