[RESOLVIDO] Objeto persistido sem submeter!

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]