JPAContainer - FieldFactory does not work for new Items

Hi Everyone out there,

I am having a problem with the JPAContainer’s FieldFacotry. It creates “simple” TextFields for all fields even for @ManyToOne, @OneToMany, etc. - as you can see in the screenshot. Should it not create a Native Select for the UserGroup field?

I followed the JPAContainer tutorial and the instructions given in the Book of Vaadin. I guess there is something very simple which I have overseen or which I do not understand. Would be nice if someone can point me in the right direction.

Thanks for your help.

Regards
Kurt

My Entities:


@Entity
public class UserGroup implements Serializable {
       
        @Id
        private String userGroupUUID;
        
        private String name;
        
        private String description;
        
        @OneToMany(mappedBy = "userGroup")
        private Set<AppUser> appUsers;

        public UserGroup() {
                userGroupUUID = UUID.randomUUID().toString();                
        }
       
        ........

@Entity
public class AppUser implements Serializable {
        
        @Id
        private String appUserUUID;
        
        private String name;
        
        private String description;
        
        @NotNull
        @ManyToOne
        private UserGroup userGroup;

        public AppUser() {
                appUserUUID = UUID.randomUUID().toString();
        }

        .......

The Editor:


public class AppUserEditor extends Window implements Button.ClickListener {

        private final Item appUserItem;

        private Form editorForm;

        private Button saveButton;
        private Button cancelButton;

        public AppUserEditor(Item appUserItem) {
                this.appUserItem = appUserItem;

                editorForm = new Form();
                
                // Use a JPAContainer field factory
                //  - no configuration is needed here according to the Book of Vaadin
                final FieldFactory fieldFactory = new FieldFactory();
                editorForm.setFormFieldFactory(fieldFactory);
                editorForm.setItemDataSource(appUserItem);
                
                editorForm.setWriteThrough(false);
                editorForm.setImmediate(true);

                saveButton = new Button("Save", this);
                cancelButton = new Button("Cancel", this);
                editorForm.getFooter().addComponent(saveButton);
                editorForm.getFooter().addComponent(cancelButton);

                getContent().setSizeUndefined();
                addComponent(editorForm);
                setCaption("AppUser Editor");
        }

        @Override
        public void buttonClick(ClickEvent event) {
                if (event.getButton() == saveButton) {
                        editorForm.commit();
                        fireEvent(new EditorSavedEvent(this, appUserItem));
                } else if (event.getButton() == cancelButton) {
                        editorForm.discard();
                }
                close();
        }

        public void addListener(EditorSavedListener listener) {
                try {
                        Method method = EditorSavedListener.class.getDeclaredMethod(
                                "editorSaved", new Class[]{EditorSavedEvent.class});
                        addListener(EditorSavedEvent.class, listener, method);
                } catch (final java.lang.NoSuchMethodException e) {
                        // This should never happen
                        throw new java.lang.RuntimeException("Internal error, editor saved method not found");
                }
        }

        public void removeListener(EditorSavedListener listener) {
                removeListener(EditorSavedEvent.class, listener);
        }

        public static class EditorSavedEvent extends Component.Event {

                private Item savedItem;

                public EditorSavedEvent(Component source, Item savedItem) {
                        super(source);
                        this.savedItem = savedItem;
                }

                public Item getSavedItem() {
                        return savedItem;
                }
        }

        public interface EditorSavedListener extends Serializable {

                public void editorSaved(EditorSavedEvent event);
        }

}

And finally the Main Window:


public class MainWindow extends Window {

        private JPAContainer<UserGroup> userGroups = JPAContainerFactory.makeJndi(UserGroup.class);
        private JPAContainer<AppUser> appUsers = JPAContainerFactory.makeJndi(AppUser.class);

        public MainWindow() {
                super("MainWindow");
                init();
        }

        private void init() {
                VerticalLayout contentRoot = (VerticalLayout) getContent();
                contentRoot.setSizeUndefined();

                /*
                 * UserGroup.
                 */
                Table userGroupTable = new Table("User Groups", userGroups);
                contentRoot.addComponent(userGroupTable);

                Button buNewUserGroup = new Button("Create new UserGroup");
                contentRoot.addComponent(buNewUserGroup);
                buNewUserGroup.addListener(new Button.ClickListener() {

                        @Override
                        public void buttonClick(ClickEvent event) {
                                final BeanItem<UserGroup> newUserGroupItem = new BeanItem<UserGroup>(new UserGroup());
                                UserGroupEditor userGroupEditor = new UserGroupEditor(newUserGroupItem);
                                userGroupEditor.addListener(new UserGroupEditor.EditorSavedListener() {
                                        @Override
                                        public void editorSaved(UserGroupEditor.EditorSavedEvent event) {
                                                userGroups.addEntity(newUserGroupItem.getBean());
                                        }
                                });
                                getApplication().getMainWindow().addWindow(userGroupEditor);
                        }

                });

                /*
                 * AppUser.
                 */
                Table appUserTable = new Table("App Users", appUsers);
                contentRoot.addComponent(appUserTable);

                Button buNewAppUser = new Button("Create new AppUser");
                contentRoot.addComponent(buNewAppUser);
                buNewAppUser.addListener(new Button.ClickListener() {

                        @Override
                        public void buttonClick(ClickEvent event) {
                                final BeanItem<AppUser> newAppUserItem = new BeanItem<AppUser>(new AppUser());
                                AppUserEditor appUserEditor = new AppUserEditor(newAppUserItem);
                                appUserEditor.addListener(new AppUserEditor.EditorSavedListener() {
                                        @Override
                                        public void editorSaved(AppUserEditor.EditorSavedEvent event) {
                                                appUsers.addEntity(newAppUserItem.getBean());
                                        }
                                });
                                getApplication().getMainWindow().addWindow(appUserEditor);
                        }
                        
                });

        }

}

I found out, that when I try to edit an (existing) item the JPAContainer FieldFactory works.

But when creating a new item it still does not work. Why is that? Any ideas? I followed section
6.2 Creating an Item
in the JPAContainer tutorial.

P.S.: I changed the subject from
JPAContainer - FieldFactory does not create the “right” Fields
to
JPAContainer - FieldFactory does not work for new items
.


                final Table userGroupTable = new Table("User Groups", userGroups);
                contentRoot.addComponent(userGroupTable);
                userGroupTable.setSelectable(true);
                userGroupTable.setImmediate(true);
                userGroupTable.addListener(new Table.ValueChangeListener() {
                        @Override
                        public void valueChange(ValueChangeEvent event) {
                                Item item = userGroups.getItem(event.getProperty().getValue());
                                [b]
getApplication().getMainWindow().addWindow(new UserGroupEditor(item)); // This works
[/b]
                        }
                });                 

                Button buNewUserGroup = new Button("Create new UserGroup");
                contentRoot.addComponent(buNewUserGroup);
                buNewUserGroup.addListener(new Button.ClickListener() {

                        @Override
                        public void buttonClick(ClickEvent event) {
                                final BeanItem<UserGroup> newUserGroupItem = new BeanItem<UserGroup>(new UserGroup());
                                UserGroupEditor userGroupEditor = new UserGroupEditor(newUserGroupItem);
                                userGroupEditor.addListener(new UserGroupEditor.EditorSavedListener() {
                                        @Override
                                        public void editorSaved(UserGroupEditor.EditorSavedEvent event) {
                                                [b]
userGroups.addEntity(newUserGroupItem.getBean()); // This does not work! TextFields only.
[/b]
                                        }
                                });
                                getApplication().getMainWindow().addWindow(userGroupEditor);
                        }

                });

Two things: First the tutorial leads in a “wrong” direction and second one should RTFM. :smiley:

First:
The tutorial says you should create a BeanItem and pass it to your editor. Well, in general this is not wrong. BUT the
FieldFactory
checks if the passed item is of type JPAContainerItem. If it is it does the trick. If not, well no trick. Thus BeanItem → no trick.

Second:
JPAContainer has a method called
createEntityItem
. It creates a new EntityItem without adding it to the container. So exactly what I need. But I was that focused on the tutorial I oversaw this method completely.

So here is how to add new Entities:


                Button buNewAppUser = new Button("Create new AppUser");
                contentRoot.addComponent(buNewAppUser);
                buNewAppUser.addListener(new Button.ClickListener() {

                        @Override
                        public void buttonClick(ClickEvent event) {
                                EntityItem newAppUserItem = appUsers.createEntityItem(new AppUser());
                                AppUserEditor appUserEditor = new AppUserEditor(newAppUserItem);
                                appUserEditor.addListener(new AppUserEditor.EditorSavedListener() {
                                        @Override
                                        public void editorSaved(AppUserEditor.EditorSavedEvent event) {
                                                appUsers.addItem(event.getSavedItem());
                                        }
                                });
                                getApplication().getMainWindow().addWindow(appUserEditor);
                        }
                        
                });

Happy Easter