2013-07-12 2 views
12

지연로드가있는 <p:dataTable>이 있습니다. 두 열에는 각각 <p:selectOneMenu>이 있습니다.p : selectOneMenu를 기반으로 p : dataTable의 각 행에있는 selectOneMenu

첫 번째 열은 국가 목록을 포함하고 두 번째 열은 데이터베이스에서 상태 목록을 보유합니다.

는 I 번째 메뉴 (상태리스트를 포함하는 것) 들만 상태를 데이터 에서 첫번째 메뉴 국가 각 행를 대응하는 데이터 테이블의 각 행를 표시하기를 원한다 표.

편집 모드에서 메뉴의 국가가 변경되면 해당 국가에 해당하는 상태가 해당 현재 행의 해당 메뉴에 채워 져야합니다.

데이터 테이블의 각 행에서 해당 국가에 해당하는 상태 목록을로드하는 방법은 무엇입니까?


데이터 테이블의이 두 열은 내가 어떻게 달성했는지에 대한 정확한 아이디어가 없기 때문에 불완전한 것으로 남습니다.

<p:column> 
    <p:cellEditor> 
     <f:facet name="output"> 
      <h:outputText value="#{row.state.country.countryName}"/> 
     </f:facet> 

     <f:facet name="input"> 
      <p:selectOneMenu value="#{row.state.country}"> 
       <f:selectItems var="country" 
           value="#{cityBean.selectedCountries}" 
           itemLabel="#{country.countryName}" 
           itemValue="#{country}"/> 

       <p:ajax update="states" listener="#{cityBean.getStates}"/> 
      </p:selectOneMenu> 
     </f:facet> 
    </p:cellEditor> 
</p:column> 

<p:column> 
    <p:cellEditor> 
     <f:facet name="output"> 
      <h:outputText value="#{row.state.stateName}"/> 
     </f:facet> 

     <f:facet name="input"> 
      <p:selectOneMenu id="states"> 

       <f:selectItems var="state" 
           value="#{cityBean.selectedStates}" 
           itemLabel="#{state.stateName}" 
           itemValue="#{state}"/> 
      </p:selectOneMenu> 
     </f:facet> 
    </p:cellEditor> 
</p:column> 

cityBean.selectedCountries이 필요하지만 cityBean.selectedStates도 불필요하며 다른 메뉴의 국가에 해당 만 상태를 검색하기 위해 수정해야 데이터베이스에서 모든 상태를 검색하는 모든 국가를 검색합니다.

여기에서 어떻게 진행할 수 있습니까?

+0

여기 보조 클래스를 활용합니다. 당신의'countryId'는 당신의 관리 빈에 직접적으로 위치해 있지 않지만, 보조 클래스에 있습니다. (테이블을 만드는 데 사용하는리스트는이 보조 도구들로 구성되어 있습니다.) 그런 다음 변경된 행을 수신하는 아약스 이벤트 리스너 (이 이벤트는 MB로 전송 됨)가 있습니다. MB는 변경된 보조 객체 (countryId)를 가져 와서 보조 객체에 들어가는 업데이트 된 상태 목록을로드합니다. –

답변

12

제어기 당신의 XHTML에서

를 (둘 다 지금은 같은 열에 하나의 메뉴의를 선택 넣었습니다)이 시도 초기 솔루션이 작동하는 동안에는 사실 비효율적입니다. 이 접근법은 기본적으로 CountryState 테이블의 전체 객체 그래프 (예 : 순환 참조 포함)가 JSF 뷰 또는 세션 당 Java 메모리에 완전히로드되도록 요구합니다. 150 개국 중 5 개국 (이론적으로 5 개 주 목록은 150 개 주 목록 대신 충분했을 것입니다).

귀하의 기능 및 기술적 요구 사항에 대한 완전한 통찰력이 없습니다. 아마 당신은 실제로 150 개국 모두를 동시에 사용하고있을 것입니다. 아마도 당신은 많은 페이지를 가지고 있습니다. (적어도 "많은") 국가와 주들이 필요합니다. 아마도 모든 서버와 국가가 모든 JSF 뷰와 메모리의 HTTP 세션에서 쉽게 복제 될 수 있도록 충분한 메모리가있는 최첨단 서버 하드웨어를 갖추고있을 것입니다. 그런 경우가 아니라면

, 열심히 모든 단일 국가의 국가 목록 (즉 @OneToMany(fetch=LAZY)Country#statesState#cities에 사용되어야한다)를 가져 하지에 도움이 될 것입니다. 국가 및 주 목록이 1 년에 거의 변하지 않는 정적 데이터 (가능한 경우) 당 최소한 배포 당 변경 될 수 있다고 가정 할 때 응용 프로그램 범위의 Bean에 저장하는 것이 낫습니다. 모든 JSF보기 또는 HTTP 세션에서 복제되지 않고 모든보기 및 세션.

답변을 계속하기 전에 코드에 논리 오류가 있음을 알려드립니다. 도시 목록을 편집 중이므로 #{row}이 본질적으로 #{city} 인 경우 드롭 다운 입력 값에 #{city.state.country}의 상태를 통해 해당 국가를 참조하는 것이 이상합니다. 표시를 위해 작동 할 수는 있지만 편집/저장에는 작동하지 않습니다. 기본적으로 도시 단위가 아닌 주 단위로 국가를 변경합니다. 현재 선택된 상태는 현재 반복되는 도시 대신 새 국가를 얻습니다. 이 변화는이주의 모든 도시에 반영 될 것입니다!

이 데이터 모델을 계속 사용하려는 경우 사실 쉽지 않습니다.이상적으로 Country 속성이 City에 있고 변경 사항이 도시의 State 속성에 영향을 미치지 않도록하고 싶습니다. @Transient으로 만들면 JPA에서는 기본적으로 @Column으로 간주하지 않습니다.

@Transient // This is already saved via City#state#country. 
private Country country; 

public Country getCountry() { 
    return (country == null && state != null) ? state.getCountry() : country; 
} 

public void setCountry(Country country) { 
    this.country = country; 

    if (country == null) { 
     state = null; 
    } 
} 

모두 모두, 당신은 궁극적으로이 있어야한다 (무관/기본/명백한 간결함을 위해 생략 속성) :

@Named 
@ApplicationScoped 
public class ApplicationBean { 

    private List<Country> countries; 
    private Map<Country, List<State>> statesByCountry; 

    @EJB 
    private CountryService countryService; 

    @EJB 
    private StateService stateService; 

    @PostConstruct 
    public void init() { 
     countries = countryService.list(); 
     statesByCountry = new HashMap<>(); 
    } 

    public List<Country> getCountries() { 
     return countries; 
    } 

    public List<State> getStates(Country country) { 
     List<State> states = statesByCountry.get(country); 

     if (states == null) { 
      states = stateService.getByCountry(country); 
      statesByCountry.put(country, states); 
     } 

     return states; 
    } 

} 

이 같은 #{applicationBean} 뭔가

<p:dataTable value="#{someViewScopedBean.cities}" var="city"> 
    ... 
    <p:selectOneMenu id="country" value="#{city.country}"> 
     <f:selectItems value="#{applicationBean.countries}" /> 
     <p:ajax update="state" /> 
    </p:selectOneMenu> 
    ... 
    <p:selectOneMenu id="state" value="#{city.state}"> 
     <f:selectItems value="#{applicationBean.getStates(city.country)}" /> 
    </p:selectOneMenu> 
    ... 
</p:dataTable> 

(이것은 게으른 로딩 접근 방식이며, 즉시 @PostConstruct에서 모두 가져올 수 있습니다. 더 나은 것을 확인하십시오)

+0

완전한 예제/설명, 훌륭하게 작동했습니다. 감사. – Tiny

+0

부수적으로, 나는 일반적으로 정당한 사유없이 유예 기간을 사용하지 않는다. 내가하고있는 것을 자극하는 것이 있습니까? :) – Tiny

+3

당신을 진심으로 환영합니다. 현상금에 대해서는 문제 없습니다. 질문이 더 이상 "추천"목록에 머무르면, 결국 당신은 소비 된 명성을 되 살릴 수있는 최단 기량을 가질 가능성이 높아집니다. – BalusC

0

이 경우 매우 간단합니다. 더 이상 코딩 할 필요가 없습니다. 상태 메뉴에서 다음과 같이 수정해야합니다.

은 다음과 같이 수정해야합니다. 이후

<f:selectItems var="state" value="#{row.state.country.stateTableSet}" 
       itemLabel="#{state.stateName}" itemValue="#{state}" 
       itemLabelEscaped="true" rendered="true"/> 

는 엔티티 객체 (이 경우 row)는 차례로 최종 단 명백 그 country 대응 미국 목록을 포함 country의 객체를 포함 state에 내장 된 객체를 포함한다.

그래서

cityManagedBean.selectedStates 

별도의 관리 빈 방법은 지금 stateTableSet이 개체의 목록을 포함하는 Set<StateTable> 인 경우 모두에서 요구하고

row.state.country.stateTableSet 

으로 수정 될 필요하지 않습니다 StateTable 엔티티


또한, <p:ajax>listener 더 이상 필요하지 않습니다. 그것은 다음과 같이 보일 것입니다.

<p:ajax update="cmbStateMenu"/> 

국가 메뉴에서 항목 (국가)을 선택하면 상태 메뉴를 업데이트하기위한 목적으로 만 존재합니다.


해당 코드는 이제 다음과 같아야합니다.

<p:column id="country" headerText="Country" resizable="true" sortBy="#{row.state.country.countryName}" filterBy="#{row.state.country.countryName}" filterMatchMode="contains" filterMaxLength="45"> 
    <p:cellEditor> 
     <f:facet name="output"> 
      <h:outputLink value="Country.jsf"> 
       <h:outputText value="#{row.state.country.countryName}"/> 
       <f:param name="id" value="#{row.state.country.countryId}"/> 
      </h:outputLink>                     
     </f:facet> 
     <f:facet name="input"> 
      <p:selectOneMenu id="cmbCountryMenu" converter="#{countryConverter}" value="#{row.state.country}" label="Country" required="true" filter="true" filterMatchMode="contains" effect="fold" rendered="true" editable="false" style="width:100%;"> 
       <f:selectItems var="country" value="#{cityManagedBean.selectedCountries}" itemLabel="#{country.countryName}" itemValue="#{country}" itemLabelEscaped="true" rendered="true"/> 
       <p:ajax update="cmbStateMenu"/> 
      </p:selectOneMenu> 
     </f:facet> 
    </p:cellEditor> 
</p:column> 

<p:column id="state" headerText="State" resizable="false" sortBy="#{row.state.stateName}" filterBy="#{row.state.stateName}" filterMatchMode="contains" filterMaxLength="45"> 
    <p:cellEditor> 
     <f:facet name="output"> 
      <h:outputLink value="State.jsf"> 
       <h:outputText value="#{row.state.stateName}"/> 
       <f:param name="id" value="#{row.state.stateId}"/> 
      </h:outputLink> 
     </f:facet> 
     <f:facet name="input"> 
      <p:selectOneMenu id="cmbStateMenu" converter="#{stateConverter}" value="#{row.state}" label="State" required="true" filter="true" filterMatchMode="contains" effect="fold" rendered="true" editable="false" style="width:100%;"> 
       <f:selectItems var="state" value="#{row.state.country.stateTableSet}" itemLabel="#{state.stateName}" itemValue="#{state}" itemLabelEscaped="true" rendered="true"/> 
      </p:selectOneMenu> 
     </f:facet> 
    </p:cellEditor> 
</p:column> 

사과 : 나는 도시의 목록을 검색 한 질문에 언급하지 않았다.

+0

이것이 효과가있을 수 있지만, 이는 본질적으로 데이터 복제이므로 메모리 비효율적입니다. – BalusC

+0

다른 정확한 방법을 알고 싶습니까? – Tiny

+0

JPA 캐시 + 응용 프로그램 범위의 bean에서 매개 변수화 된 getter (또는 CDI를 사용하는 경우'@ Produces' 메서드, 스프링을 사용하지 않는 것과 같은 점이 무엇인지 확실하지 않음) – BalusC

0

선택한 국가를 기반으로 상태 목록을 가져와야합니다. 이를 위해 valueChangeListener가 필요합니다.

<p:column id="state" headerText="State" sortBy="#{row.state.stateName}" filterBy="#{row.state.stateName}"> 
<p:cellEditor> 
     <f:facet name="output"> 
     <f:facet name="output"> 
        <h:outputLink value="State.jsf"> 
         <h:outputText value="#{row.state.stateName}"/> 
         <f:param name="id" value="#{row.state.stateId}"/> 
        </h:outputLink> 
      </f:facet> 
     <f:facet name="input"> 
     <h:selectOneMenu id="cmbCountryMenu" style="width:100px" value="#{row.state.country}" converterMessage="Error message." label="Country" valueChangeListener = "#{countryController.handleCountrySelect}" immediate="true" converter="#{countryConverter}"> 
      <f:selectItem itemLabel = "Select" itemValue="#{null}" /> 
      <f:selectItems var="country" value="#{cityManagedBean.selectedCountries}" itemLabel="#{country.countryName}" itemValue="#{country}"/> 
      <p:ajax update="cmbStateMenu" /> 
     </h:selectOneMenu> 
     <h:selectOneMenu style="width:100px" value="#{row.state}" valueChangeListener = "#{stateController.handleStateSelect}" immediate="false" id="cmbStateMenu" converter = "#{stateConverter}"> 

      <f:selectItems var="state" value="#{row.state.country.stateTableSet}" itemLabel="#{state.stateName}" itemValue="#{state}" itemLabelEscaped="true" rendered="true"/> 
      <p:ajax update="@this" /> 
     </h:selectOneMenu> 
     </f:facet> 
    </p:cellEditor> 

당신에

public void handleCountrySelect(ValueChangeEvent event) 
{ 
    setStates(((Country) event.getNewValue())); 
} 
+0

이 경우, 내가 게시 한 응답이 예상 된 기능을 수행하지만 'row.state.country.stateTableSet'과 같은 EL 표현식 (이 게시물 이후에'java.util.Set'는'java.util.List'로 변경되었습니다)은 생략하거나 제거해야합니다. 그것이 매우 비싸기 때문에 그것의 전적으로. 원격 EJB의 경우에는, ** ** ** ** ** ** ** ** ** ** 긴급하게 **로드 된 관계 나 JPA에서 값 비싼 콜렉션 가져 오기가 필요합니다. 이것은 구현하기에 최악의 일이며 ** 사용하지 않는 것이 좋습니다. **꼭 필요한. – Tiny

+1

노력에 감사드립니다. – Tiny

관련 문제