Hi,
I cannot control the synchronization from a persistent object to the database. The more I
read and the more I try out the more I get confused. I hope that somebody can give me a
helping hint.
For testing I generated a simple ear project with seam-gen (Seam 2.0.0.GA) and added an
entity class and a stateful session bean.
The use case:
A logged in user wants to change his email address (unique field in table).
A server-side validation with a database selection is effected and fails if the new email
address exists in the database.
What happens:
If I enter a new email and press Save, a synchronization (flush) takes place just before
the selection of the existing email addresses has been started. In FlushMode.AUTO this is
normal, but not in FlushMode.MANUAL. The effect is that EVERY new email is known to the
system and the validation does not work as it should.
The entity class MyUser is:
| package some.model;
|
| // imports...
|
| @Entity
| @Table
| @Name("user")
| public class MyUser implements Serializable {
|
| @Id @GeneratedValue
| private Long id = null;
|
| // username is the business key
| @Column(name="username", nullable = false, unique = true)
| private String username;
|
| @Email
| @Column(name="email", nullable = false, unique = true)
| private String email;
|
| private String firstname;
| private String lastname;
|
| public MyUser() {
| }
|
| //... getter and setter ...
|
| }
|
The stateful session bean implementation is:
| package some.controller;
|
| // imports ...
|
| @Name( "userAction" )
| @Scope( ScopeType.CONVERSATION )
| @Stateful
| public class UserActionBean implements Serializable, UserAction {
| private static final long serialVersionUID = 1L;
|
| @PersistenceContext(type=PersistenceContextType.EXTENDED)
| private EntityManager entityManager;
|
| @In(create=true)
| private transient FacesMessages facesMessages;
|
| @In( value = "user", required = false )
| @Out( value = "user", required = false )
| private MyUser selectedUser;
|
| @In
| private Identity identity;
|
| private String backupEmail;
|
| @Begin(flushMode=FlushModeType.MANUAL, join=true)
| public String selectLoggedInUser() {
| System.out.println( "Selected logged in User: " + identity.getUsername()
);
| List<MyUser> existing = (List<MyUser>) entityManager
| .createQuery("from MyUser u where u.username = :uname)")
| .setParameter("uname", identity.getUsername())
| .getResultList();
| if (existing.size() == 1) {
| selectedUser = (MyUser) existing.get(0);
| backupEmail = selectedUser.getEmail();
| System.out.println( "Redirecting to /editLoggedInUser.xhtml" );
| return "/editLoggedInUser.xhtml";
| } else {
| return "/home.xhtml";
| }
| }
|
| @End
| public String saveLoggedInUser() {
|
| // Try if email exists
| // There should NOT be a flush before the query is effected
| // But there IS a flush before the query is effected
| if (emailExists()) {
| selectedUser.setEmail(backupEmail);
| return null;
| }
|
| // Normal flush (success case)
| entityManager.flush();
|
| FacesMessages.instance().add( "#{user.username} has been saved." );
| return "/home.xhtml";
| }
|
| private boolean emailExists() {
| List<String> existing = (List<String>)
| entityManager.createQuery("from MyUser u where u.email = :email)")
| .setParameter("email", selectedUser.getEmail())
| .getResultList();
| if(existing.size() != 0) {
| facesMessages.add("#{messages['NewEmailExists']}" +
selectedUser.getEmail());
| return true;
| }
| return false;
| }
|
| @Remove @Destroy
| public void destroy() {}
| }
|
Extension of "menu.xhtml" to start the conversation:
| ...
| <rich:toolBarGroup rendered="#{identity.loggedIn}">
| <s:link action="#{userAction.selectLoggedInUser}"
value="#{messages['MyUser']}"/>
| </rich:toolBarGroup>
| ...
|
The form "editLoggedInUser.xhtml" is:
| <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
| <ui:composition
xmlns="http://www.w3.org/1999/xhtml"
|
xmlns:s="http://jboss.com/products/seam/taglib"
|
xmlns:ui="http://java.sun.com/jsf/facelets"
|
xmlns:f="http://java.sun.com/jsf/core"
|
xmlns:h="http://java.sun.com/jsf/html"
|
xmlns:j4j="http://javascript4jsf.dev.java.net/"
|
xmlns:rich="http://richfaces.ajax4jsf.org/rich"
| template="layout/template.xhtml">
|
| <ui:define name="body">
|
| <h:messages styleClass="message" globalOnly="true"/>
|
| <h:form id="editLoggedInUser">
|
| <rich:panel>
| <f:facet
name="header">#{messages['MyUser']}</f:facet>
|
| <div class="dialog">
| <s:validateAll>
| <h:panelGrid columns="2" rowClasses="prop"
columnClasses="name,value">
|
|
<h:outputLabel>#{messages['Username']}</h:outputLabel>
| <h:outputText value="#{user.username}"/>
|
| <h:outputLabel
for="firstname">#{messages['FirstName']}</h:outputLabel>
| <s:decorate>
| <h:inputText id="firstname"
required="true"
| value="#{user.firstname}"/>
| </s:decorate>
|
| <h:outputLabel
for="lastname">#{messages['LastName']}</h:outputLabel>
| <s:decorate>
| <h:inputText id="lastname"
required="true"
| value="#{user.lastname}"/>
| </s:decorate>
|
| <h:outputLabel
for="email">#{messages['Email']}</h:outputLabel>
| <s:decorate>
| <h:inputText id="email"
required="true"
| value="#{user.email}"/>
| </s:decorate>
|
| </h:panelGrid>
| </s:validateAll>
| </div>
|
| </rich:panel>
|
| <div class="actionButtons">
| <h:commandButton id="saveLoggedInUser"
value="#{messages['Save']}"
| action="#{userAction.saveLoggedInUser}"/>
| </div>
|
| </h:form>
|
| </ui:define>
| </ui:composition>
|
The persistence.xml shows that Hibernate is the persistence provider, so MANUAL FlushMode
should work:
| ...
| <persistence-unit name="aValidationTest">
| <provider>org.hibernate.ejb.HibernatePersistence</provider>
| <jta-data-source>java:/aValidationTestDatasource</jta-data-source>
| <properties>
| <property name="hibernate.dialect"
value="org.hibernate.dialect.HSQLDialect"/>
| <property name="hibernate.hbm2ddl.auto"
value="create-drop"/>
| <property name="hibernate.show_sql" value="true"/>
| <property name="hibernate.format_sql"
value="true"/>
| <property name="jboss.entity.manager.factory.jndi.name"
value="java:/aValidationTestEntityManagerFactory"/>
| </properties>
| </persistence-unit>
| ...
|
components.xml:
| ...
| <persistence:managed-persistence-context name="entityManager"
| auto-create="true"
|
persistence-unit-jndi-name="java:/aValidationTestEntityManagerFactory"/>
| ...
|
import-dev.sql:
| insert into MyUser (ID, USERNAME, FIRSTNAME, LASTNAME, EMAIL) values (1,
'admin', 'Frederic', 'Chopin', 'one(a)email.com');
| insert into MyUser (ID, USERNAME, FIRSTNAME, LASTNAME, EMAIL) values (2,
'user', 'Baby', 'Love', 'two(a)email.com');
|
What's wrong in my thinking?
Thank you very much for all answers.
View the original post :
http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4113239#...
Reply to the post :
http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&a...