6 Replies Latest reply on Jan 25, 2008 2:27 AM by jsutherland

    modify entity after persist from same method

    jsutherland

      Just wondering if it is possible to change a newly persisted entity instance (from EntityHome) in the same action method.

      I can't seem to find any information about this anywhere. It is certainly hibernate related..

      My action method injects 3 types of EntityHome objects

      My problem is that I have to create these 3 different entities (one of each type) in this action and they all refer to each other but they need to be persisted in a certain order. So after they are all persisted I need to modify some of the relations on the new entities. Whenever I add these new relationships I get an exception:

      [lifecycle] #{Register.registerUser}: avax.ejb.EJBTransactionRolledbackException: validation failed for: com.example.User
      ...

      Caused by: org.hibernate.validator.InvalidStateException: validation failed for: com.vivapop.calendar.entity.User
      at org.hibernate.validator.event.ValidateEventListener.validate(ValidateEventListener.java:143)
      at org.hibernate.validator.event.ValidateEventListener.onPreUpdate(ValidateEventListener.java:172)
      at org.hibernate.action.EntityUpdateAction.preUpdate(EntityUpdateAction.java:217)
      at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:65)
      at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:250)


      Is it possible to do the updates in the same action after persist? If not there must be another approach that I can't see?


      Thanks for any input.


      Jon

        • 1. Re: modify entity after persist from same method
          nickarls

          Please show the code (especially the entities since the exception seems to originate from hibernate validator)

          • 2. Re: modify entity after persist from same method
            jsutherland

            I have my form for registration here:

            <!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:rich="http://richfaces.org/rich" template="layout/template.xhtml">
             <ui:define name="body">
             <h:messages globalOnly="true" styleClass="message" />
             <s:div>
             <rich:panel>
             <h:form id="register">
             <h:panelGrid style="margin: 0 auto">
             <s:decorate id="firstNameDecoration" template="layout/edit.xhtml">
             <ui:param name="style_class" value="right" />
             <ui:define name="label">First Name:</ui:define>
             <h:inputText styleClass="width200"
             value="#{userHome.instance.firstName}" required="true" />
             </s:decorate>
             <s:decorate id="lastNameDecoration" template="layout/edit.xhtml">
             <ui:param name="style_class" value="right" />
             <ui:define name="label">Last Name:</ui:define>
             <h:inputText styleClass="width200"
             value="#{userHome.instance.lastName}" required="true" />
             </s:decorate>
             <s:decorate id="emailDecoration" template="layout/edit.xhtml">
             <ui:param name="style_class" value="right" />
             <ui:define name="label">Email:</ui:define>
             <h:inputText id="email" styleClass="width200"
             value="#{userHome.instance.email}" required="true" />
             </s:decorate>
             <s:decorate id="confirmEmailDecoration"
             template="layout/edit.xhtml">
             <ui:param name="style_class" value="right" />
             <ui:define name="label">Confirm Email:</ui:define>
             <h:inputText styleClass="width200" value="" required="true">
             <f:validator validatorId="userConfirmEmail" />
             <f:attribute name="emailId"
             value="register:emailDecoration:email" />
             </h:inputText>
             </s:decorate>
             <s:decorate id="passwordDecoration" template="layout/edit.xhtml">
             <ui:param name="style_class" value="right" />
             <ui:define name="label">Password:</ui:define>
             <h:inputText id="password" styleClass="width200"
             value="#{userHome.instance.password}" required="true" />
             </s:decorate>
             <s:decorate id="confirmPasswordDecoration"
             template="layout/edit.xhtml">
             <ui:param name="style_class" value="right" />
             <ui:define name="label">Confirm Password:</ui:define>
             <h:inputText value="" required="true" styleClass="width200">
             <f:validator validatorId="userConfirmPassword" />
             <f:attribute name="passwordId"
             value="register:passwordDecoration:password" />
             </h:inputText>
             </s:decorate>
             <s:decorate id="languageDecoration" template="layout/edit.xhtml">
             <ui:param name="style_class" value="right" />
             <ui:define name="label">Language:</ui:define>
             <h:selectOneMenu value="#{localeSelector.localeString}"
             style="width: 80px; margin-right: 120px">
             <f:selectItems value="#{localeSelector.supportedLocales}"
             var="locale" label="#{locale.label}" cache="true" />
             </h:selectOneMenu>
             </s:decorate>
             <s:decorate id="timeZoneDecoration" template="layout/edit.xhtml">
             <ui:param name="style_class" value="right" />
             <ui:define name="label">Time Zone:</ui:define>
             <h:selectOneMenu value="#{userHome.instance.timeZone}"
             required="true" style="width: 150px; margin-right: 50px">
             <s:selectItems value="#{timeZoneList}" var="timezone"
             label="#{timezone.name}"
             noSelectionLabel="Select your time zone:" />
             <s:convertEntity />
             </h:selectOneMenu>
             </s:decorate>
            
             <s:div styleClass="actionButtons" style="padding-top: 50px">
             <h:commandButton id="inc" value="Register"
             action="#{Register.registerUser}" />
             <rich:spacer width="20px" />
             <h:commandButton immediate="true" id="cancel" value="Cancel"
             action="/home.xhtml" />
             </s:div>
            
             </h:panelGrid>
            
             </h:form>
             </rich:panel>
             </s:div>
             </ui:define>
            </ui:composition>
            
            




            The Action class:

            @Stateful
            @Name("Register")
            public class RegisterBean implements Register {
            
             //@Logger private Log log;
             @In(create = true)
             UserHome userHome;
             @In(create = true)
             CalendarHome calendarHome;
             @In(create = true)
             CalendarPreferenceHome calendarPreferenceHome;
             @In
             EntityManager entityManager;
             @In
             LocaleSelector localeSelector;
            
             @Begin(join = true, flushMode = FlushModeType.MANUAL)
             public String registerUser() {
             User user = userHome.getInstance();
             userHome.getInstance().setTheme(
             entityManager.find(Theme.class, new Long(2)));
             userHome.getInstance().setLocale(localeSelector.getLocaleString());
             userHome.persist();
            
             Calendar calendar = calendarHome.getInstance();
             calendar.setCreatedBy(userHome.getInstance());
             calendar.setName(userHome.getInstance().getFirstName() + " "
             + userHome.getInstance().getLastName() + "'s Calendar");
             calendar.getUsers().add(userHome.getInstance());
             calendarHome.persist();
            
             CalendarPreference calPref = calendarPreferenceHome.getInstance();
             calPref.setCalendar(calendar);
             calPref
             .setForegroundColor(entityManager
             .find(Color.class, new Long(1)));
             calPref
             .setBackgroundColor(entityManager
             .find(Color.class, new Long(3)));
             calPref.setDisplayDefault(true);
             calPref.setUser(userHome.getInstance());
             calendarPreferenceHome.persist();
            
             userHome.getInstance().getCalendars().add(calendar);
             userHome.update();
             //userHome.getEntityManager().flush();
            
             return "registerSuccess";
             }
            
             @End
             public String end() {
             return "home";
             }
            
             @Destroy
             @Remove
             public void destroy() {
             }
            }
            





            The entities:

            @Entity
            @Table(uniqueConstraints = @UniqueConstraint(columnNames = "email"))
            @Name("User")
            public class User implements Serializable {
            
             private static final long serialVersionUID = 1L;
             private Long id;
             private Integer version;
             private String firstName;
             private String lastName;
             private String email;
             private TimeZone timeZone;
             private String locale;
             private Theme theme;
             private String password;
            
             private List<Calendar> calendars = new Vector<Calendar>();
             private List<Event> events = new Vector<Event>();
            
             public String toString() {
             String tmp = "";
             tmp += this.firstName + "\n";
             tmp += this.lastName + "\n";
             tmp += this.email + "\n";
            
             return tmp;
             }
            
             @Id
             @GeneratedValue
             public Long getId() {
             return id;
             }
            
             public void setId(Long id) {
             this.id = id;
             }
            
             @Version
             public Integer getVersion() {
             return version;
             }
            
             @SuppressWarnings("unused")
             private void setVersion(Integer version) {
             this.version = version;
             }
            
             @NotNull
             @Length(max = 15)
             public String getFirstName() {
             return firstName;
             }
            
             public void setFirstName(String firstName) {
             this.firstName = firstName;
             }
            
             @NotNull
             @Length(max = 15)
             public String getLastName() {
             return lastName;
             }
            
             public void setLastName(String lastName) {
             this.lastName = lastName;
             }
            
             @NotNull
             @Email
             @UserEmail
             public String getEmail() {
             return email;
             }
            
             public void setEmail(String email) {
             this.email = email;
             }
            
             @NotNull
             @ManyToOne
             public TimeZone getTimeZone() {
             return timeZone;
             }
            
             public void setTimeZone(TimeZone timeZone) {
             this.timeZone = timeZone;
             }
            
             @NotNull
             public String getLocale() {
             return locale;
             }
            
             public void setLocale(String locale) {
             this.locale = locale;
             }
            
             @NotNull
             @ManyToOne
             public Theme getTheme() {
             return theme;
             }
            
             public void setTheme(Theme theme) {
             this.theme = theme;
             }
            
             @NotNull
             @Length(min = 5, max = 30)
             public String getPassword() {
             return password;
             }
            
             public void setPassword(String password) {
             this.password = password;
             }
            
             @ManyToMany
             public List<Calendar> getCalendars() {
             return calendars;
             }
            
             public void setCalendars(List<Calendar> calendars) {
             this.calendars = calendars;
             }
            
             @ManyToMany(mappedBy = "users")
             @OrderBy("startDate")
             public List<Event> getEvents() {
             return events;
             }
            
             public void setEvents(List<Event> events) {
             this.events = events;
             }
            }
            


            @Entity
            public class Calendar implements Serializable {
            
             private static final long serialVersionUID = 1L;
             private Long id;
             private Integer version;
             private String name;
             private User createdBy, modifiedBy;
             private List<User> users;
             private List<Schedule> schedules;
             private List<Event> events;
             private Color defaultColor;
             private boolean publicVisibility = false;
             private String description;
             private Date createdDate, modifiedDate;
            
             // run-time (transient)
             private boolean displayedInView = false;
             private CalendarPreference preference;
            
             @Transient
             public boolean isDisplayedInView() {
             return displayedInView;
             }
            
             public void setDisplayedInView(boolean displayedInView) {
             this.displayedInView = displayedInView;
             }
            
             @Transient
             public CalendarPreference getPreference() {
             return preference;
             }
            
             public void setPreference(CalendarPreference preference) {
             this.preference = preference;
             }
            
             public Calendar() {
             users = new Vector<User>();
             schedules = new Vector<Schedule>();
             events = new Vector<Event>();
             createdDate = new Date();
             }
            
             @NotNull
             public boolean isPublicVisibility() {
             return publicVisibility;
             }
            
             public void setPublicVisibility(boolean publicVisibility) {
             this.publicVisibility = publicVisibility;
             }
            
             @Id
             @GeneratedValue
             public Long getId() {
             return id;
             }
            
             public void setId(Long id) {
             this.id = id;
             }
            
             @Version
             public Integer getVersion() {
             return version;
             }
            
             @SuppressWarnings("unused")
             private void setVersion(Integer version) {
             this.version = version;
             }
            
             @Length(max = 50)
             public String getName() {
             return name;
             }
            
             public void setName(String name) {
             this.name = name;
             }
            
             @ManyToMany(mappedBy = "calendars")
             public List<User> getUsers() {
             return users;
             }
            
             public void setUsers(List<User> users) {
             this.users = users;
             }
            
             @ManyToOne
             @NotNull
             public User getCreatedBy() {
             return createdBy;
             }
            
             public void setCreatedBy(User createdBy) {
             this.createdBy = createdBy;
             }
            
             @ManyToMany(mappedBy = "calendars")
             public List<Schedule> getSchedules() {
             return schedules;
             }
            
             public void setSchedules(List<Schedule> schedules) {
             this.schedules = schedules;
             }
            
             @ManyToMany(mappedBy = "calendars")
             @OrderBy("startDate")
             public List<Event> getEvents() {
             return events;
             }
            
             public void setEvents(List<Event> events) {
             this.events = events;
             }
            
             @ManyToOne
             public Color getDefaultColor() {
             return defaultColor;
             }
            
             public void setDefaultColor(Color defaultColor) {
             this.defaultColor = defaultColor;
             }
            
             @Length(max = 255)
             public String getDescription() {
             return description;
             }
            
             public void setDescription(String description) {
             this.description = description;
             }
            
             @ManyToOne
             public User getModifiedBy() {
             return modifiedBy;
             }
            
             public void setModifiedBy(User modifiedBy) {
             this.modifiedBy = modifiedBy;
             }
            
             @NotNull
             @Temporal(TemporalType.TIMESTAMP)
             public Date getCreatedDate() {
             return createdDate;
             }
            
             public void setCreatedDate(Date createdDate) {
             this.createdDate = createdDate;
             }
            
             @Temporal(TemporalType.TIMESTAMP)
             public Date getModifiedDate() {
             return modifiedDate;
             }
            
             public void setModifiedDate(Date modifiedDate) {
             this.modifiedDate = modifiedDate;
             }
            }
            


            @Entity
            public class CalendarPreference implements Serializable {
            
             private static final long serialVersionUID = 1L;
             private Long id;
             private Integer version;
             private Calendar calendar;
             private User user;
            
             private Color foregroundColor,backgroundColor;
             private boolean displayDefault = true;
            
             @ManyToOne @NotNull
             public Color getBackgroundColor() {
             return backgroundColor;
             }
            
             public void setBackgroundColor(Color color) {
             this.backgroundColor = color;
             }
            
             @NotNull
             public boolean isDisplayDefault() {
             return displayDefault;
             }
            
             public void setDisplayDefault(boolean displayDefault) {
             this.displayDefault = displayDefault;
             }
            
             @OneToOne @NotNull
             public Calendar getCalendar() {
             return calendar;
             }
            
             public void setCalendar(Calendar calendar) {
             this.calendar = calendar;
             }
            
             @OneToOne @NotNull
             public User getUser() {
             return user;
             }
            
             public void setUser(User user) {
             this.user = user;
             }
            
             @Id @GeneratedValue
             public Long getId() {
             return id;
             }
            
             public void setId(Long id) {
             this.id = id;
             }
            
             @Version
             public Integer getVersion() {
             return version;
             }
            
             @SuppressWarnings("unused")
             private void setVersion(Integer version) {
             this.version = version;
             }
            
             @ManyToOne @NotNull
             public Color getForegroundColor() {
             return foregroundColor;
             }
            
             public void setForegroundColor(Color foregroundColor) {
             this.foregroundColor = foregroundColor;
             }
            }
            


            My EntityHome objects are just the ones generated by seam-gen and look like this:

            @Name("userHome")
            public class UserHome extends EntityHome<User>
            {
            
             @RequestParameter
             Long userId;
            
             @Override
             public Object getId()
             {
             if (userId==null)
             {
             return super.getId();
             }
             else
             {
             return userId;
             }
             }
            
             @Override @Begin
             public void create() {
             super.create();
             }
            }
            


            I have to create the entities in that order since the last 2 have @NotNull references to the first 2. After they are all succesfully created I then try to add the calendar to the User entity but I can't seem to find how to do that.

            I have tried a combination of flush modes and update(), merge(), entityManager.flush()

            I am starting to think I might need to do this differently by using a Hibernate Session instead? I don't know.

            Thanks for your reply.


            Jon


            • 3. Re: modify entity after persist from same method
              jsutherland

              I should also mention that in the action registerUser() when I remove the last few lines:

              
              userHome.getInstance().getCalendars().add(calendar);
              userHome.update();
              //userHome.getEntityManager().flush();
              
              


              Everything works as expected.

              • 4. Re: modify entity after persist from same method
                pmuir

                Why don't you just persist them all at the end? Hibernate doesn't care if the entity is persistent or not when you set up relationships. Or do you need some database trigger to fire?

                • 5. Re: modify entity after persist from same method
                  jsutherland

                  Pete,

                  Because when I do that I get transient instance problems:

                  19:33:15,893 INFO [STDOUT] Hibernate:
                   insert
                   into
                   User_Calendar
                   (users_id, calendars_id)
                   values
                   (?, ?)
                  19:33:15,895 ERROR [AbstractFlushingEventListener] Could not synchronize database state with session
                  org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.vivapop.calendar.entity.Calendar
                   at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:219)
                   at org.hibernate.type.EntityType.getIdentifier(EntityType.java:397)
                   at org.hibernate.type.ManyToOneType.nullSafeSet(ManyToOneType.java:78)
                   at org.hibernate.persister.collection.AbstractCollectionPersister.writeElement(AbstractCollectionPersister.java:755)
                   at org.hibernate.persister.collection.AbstractCollectionPersister.recreate(AbstractCollectionPersister.java:1143)
                   at org.hibernate.action.CollectionRecreateAction.execute(CollectionRecreateAction.java:26)
                   at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:250)
                   ...
                  


                  I searched hibernate forums about this and couldn't seem to find anything related ?



                  • 6. Re: modify entity after persist from same method
                    jsutherland

                    Oh no I feel really stupid but I think I found the problem to be a bug in my custom hibernation validation. I wasn't thinking about how my hibernate validator was working on an update.

                    I guess what had me fooled was that I didn't recognize the org.hibernate.validator.InvalidStateException as related to my custom validator since I had assumed the stack trace or exception would give me a clue that it was in my own validator class. ugh.

                    I really should have thought more about my custom validator. Thanks for looking at this issue for me anyway, I really appreciate it.