Pessoal, to com um problema bizarro nas minhas combos aninhadas aqui, e pior que só notei isso depois que já tinha feito essas combos em diversos lugares.
- Na minha app Cidade, tenho combos aninhadas de País, Estado e para cadastrar uma cidade.
- Ao cadastrar uma nova cidade, as combos carregam normalmente, em sequencia, e a cidade é cadastrada normalmente.
- Ao editar, as informações do banco são trazidas, por exemplo: país (Brasil), estado (São Paulo), cidade (Santos).
- A merda que fiz acontece quando eu simplesmente altero a combo país para EUA, por exemplo, e o estado São Paulo passa a pertencer aos EUA!!! Sem submeter, não clico em botão nenhum, só de alterar a combo é feito o update.
- Aí a combo de estados dos EUA passa a ter, além dos seus próprios estados, a opção São Paulo, pois já está salvo no banco!!!
- No console aparece o comando: “update app_java.state set country_id=?, name=? where state_id=?”
Segue meu código:
Entity
@Entity
@Table(name = "city")
public class City implements Serializable {
private static final long serialVersionUID = 1L;
private Integer cityId;
private State state;
private String name;
public City(){}
public City(State state, String name) {
this.state = state;
this.name = name;
}
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "city_id", unique = true, nullable = false)
public Integer getCityId() {
return cityId;
}
public void setCityId(Integer cityId) {
this.cityId = cityId;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "state_id", nullable = false)
@NotNull
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
@Column(name = "name", nullable = false, length = 50)
@NotNull
@Length(max = 50)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((cityId == null) ? 0 : cityId.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof City))
return false;
City other = (City) obj;
if (getCityId() == null) {
if (other.getCityId() != null)
return false;
} else if (!getCityId().equals(other.getCityId()))
return false;
return true;
}
}
MB
@Name("cityAction")
@Scope(ScopeType.CONVERSATION)
@Restrict("#{identity.loggedIn}")
public class CityAction {
@In
private EntityManager entityManager;
@DataModel
private List<City> citys;
@DataModelSelection
@Out(required=false)
private City city;
@Out(required = false)
private List<State> loadStates;
@Factory("citys")
@SuppressWarnings("unchecked")
public void listCitys() {
citys = entityManager.createQuery("" +
"select c from City c order by c.name").getResultList();
}
@Begin
public String addCity() {
city = new City();
city.setState(new State());
return "addCity";
}
@Begin
public String editCity() {
entityManager.refresh(city);
return "editCity";
}
@End
public String saveCity() {
if (city.getCityId() == null) {
entityManager.persist(city);
} else {
entityManager.merge(city);
}
return "saveCity";
}
@Factory("loadStates")
@SuppressWarnings("unchecked")
public void loadStates() {
setLoadStates((entityManager.createQuery(
"select s from State s " +
"where s.country.countryId = "
+ city.getState().getCountry().getCountryId())
.getResultList()));
}
// Getters and Setters
}
JSF
<ui:define name="body">
<h:form id="city" styleClass="edit">
<rich:panel>
<f:facet name="header">Cidade</f:facet>
<s:decorate id="countryDecoration" template="../layout/edit.xhtml">
<ui:define name="label">Pais</ui:define>
<h:selectOneMenu value="#{city.state.country}"
id="country" label="Pais" required="true">
<s:selectItems var="_country"
value="#{countrys}"
label="#{_country.name}"
noSelectionLabel="Selecione" />
<s:convertEntity />
<a:support event="onchange"
reRender="countryDecoration, loadStates"
ajaxSingle="true"
action="#{cityAction.loadStates}" />
</h:selectOneMenu>
</s:decorate>
<s:decorate id="stateDecoration" template="../layout/edit.xhtml">
<ui:define name="label">Estado</ui:define>
<a:outputPanel id="loadStates">
<h:selectOneMenu value="#{city.state}"
id="state" required="true" label="Estado"
rendered="#{not empty city.state.country.countryId}">
<s:selectItems var="_state"
value="#{loadStates}"
label="#{_state.name}"
noSelectionLabel="Selecione" />
<s:convertEntity />
<a:support event="onblur" reRender="stateDecoration"
bypassUpdates="true" ajaxSingle="true" />
</h:selectOneMenu>
</a:outputPanel>
</s:decorate>
<s:decorate id="nameDecoration" template="../layout/edit.xhtml">
<ui:define name="label">Nome</ui:define>
<h:inputText id="name" required="true" size="50" maxlength="50"
value="#{city.name}" label="Nome">
<a:support event="onblur" reRender="nameDecoration"
bypassUpdates="true" ajaxSingle="true" />
</h:inputText>
</s:decorate>
</rich:panel>
<div class="actionButtons">
<h:commandButton id="save" value="Salvar" action="#{cityAction.saveCity}" />
</div>
</h:form>
</ui:define>
Se alguém tiver paciencia para me dar uma força, agradeço, pois tenho combos iguais a essa em toda minha aplicação!!
Abraços!!
Edit: já achei a solução, por padrão o Flush vem setado como Auto, no método edit() eu o setei como Manual e deu um em.flush() na hora de salvar.
[code]@Begin(flushMode = FlushModeType.MANUAL) <----------
public String editCity() {
entityManager.refresh(city);
return “editCity”;
}
@End
public String saveCity() {
if (city.getCityId() == null) {
entityManager.persist(city);
} else {
entityManager.flush(); <----------
entityManager.merge(city);
}
return “saveCity”;
}[/code]