JPA, JPAContainer and FieldFactory

Hi all

I’m using the jpacontainer.archetype version 2.0 and I’m trying to do the following mapping:
My Classes (Keyword, Translation, Country):

@Entity
public class Keyword  implements Serializable {
    
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="ID")
    private Long m_Id;
    @Column(name="keyword", unique=true)
    private String m_Keyword;
    @ElementCollection
    private Set<Translation> translations;
    public Keyword() {
    
    }
    
    public Keyword(String keyword){
        m_Keyword = keyword;
    }

    public Long getId() {
        return m_Id;
    }

    public void setId(Long id) {
        m_Id = id;
    }

    public String getKeyword() {
        return m_Keyword;
    }

    public void setKeyword(String keyword) {
        m_Keyword = keyword;
    }

    public Set<Translation> getTranslations() {
        return translations;
    }

    public void setTranslations(ArrayList<Translation> translations) {
        translations.addAll(translations);
    }
    
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((m_Id == null) ? 0 : m_Id.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Keyword other = (Keyword) obj;
        if (m_Id == null) {
            if (other.m_Id != null)
                return false;
        } else if (!m_Id.equals(other.m_Id))
            return false;
        return true;
    }



@Embeddable
public class Translation implements Serializable {
    
    @Column(name="status")
    @Enumerated(EnumType.STRING)
    private TranslationStatus m_Status;
    @OneToOne(cascade=CascadeType.PERSIST)
    @JoinColumn(name="country_id")
    private Country m_Country;

    /**
     * @param the {@link TranslationStatus} of the translation
     * @param inherent {@link Country} of the translation
     * @param translated value
     */
    public Translation(Country country, String value) {
        m_Translation = value;
        m_Country = country;
       
    }
    /**
     * creates a new instance of and sets the status to untranslated
     */
    public Translation(){
        m_Status = TranslationStatus.untranslated;
    }

    public TranslationStatus getStatus() {
        return m_Status;
    }


    public void setStatus(TranslationStatus status) {
        m_Status = status;
    }


    public Language getLanguage() {
        return m_Language;
    }


    public void setLanguage(Language language) {
        m_Language = language;
    }

    public Country getCountry() {
        return m_Country;
    }


    public void setCountry(Country country) {
        m_Country = country;
    }


    public Bundle getBundle() {
        return m_Bundle;
    }


    public void setBundle(Bundle bundle) {
        m_Bundle = bundle;
    }

    public String getTransValue() {
        return m_Translation;
    }
    public void setTransValue(String transValue) {
        m_Translation = transValue;
    }


@Entity
public class Country {
    
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="ID")
    private Long m_Id;
    @Column(name="name")
    private String m_Name;
    @Column(name="code")
    @Enumerated(EnumType.STRING)
    private CountryCode m_Code;

    public Country() {
        
    }

    public Long getId() {
        return m_Id;
    }

    public void setId(Long id) {
        m_Id = id;
    }

    public String getName() {
        return m_Name;
    }

    public void setName(String name) {
        m_Name = name;
    }

    public CountryCode getCode() {
        return m_Code;
    }

    public void setCode(CountryCode code) {
        m_Code = code;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((m_Id == null) ? 0 : m_Id.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Country other = (Country) obj;
        if (m_Id == null) {
            if (other.m_Id != null)
                return false;
        } else if (!m_Id.equals(other.m_Id))
            return false;
        return true;
    }

}

As you can see I have a Keyword class which maps a relationship with the Translation class. The Translation class is annotated with @Embeddable and maps a relationship with the Country class. Now when I let my JPAContainer FieldFactory create the form, I’m getting the result in the Attachement. What I need is a combobx for the field country, that is populated with the JPAContainer (name property) informations. Now I think overriding the JPAContainer FieldFactory “create Field” method, is the right way to go if I’m not mistaken? Or is my mapping with the @embeddable annotation the wrong approach? What’s the best/right way to get the Country name property into the translation @embedded class?

I highly appreciate your help

greetings nik

13567.jpg

hmm, maybe you can try to make the relation to Country from @OneToOne to @ManyToOne in Translation, that should generate you a NativeSelect, and for me, the @ManyToOne relationship makes more sense.

hi haijian

thanks for your feedback! I changed the relation to @ManyToOne and it did generate a native select like you said. But the field is empty, how can I get the Country attribute “name” from the JPAContainer as the datasource for the native select?

thanks in advance

I’ve tried the following:

@Override
public Field createField(Item item. Object propertyId, uiContext);

Field field = super.createField(item, propertyId, uiContext);

if(field instanceof NativeSelect) {
JPAContainer country = JPAContaiinerFactory.make(Country.class, TranslationUI.PERSISTENCE_UNIT);
((NativeSelect) field).setContainerDataSource(country);
((NativeSelect) field).setItemCaptionPropertyId(“name”);
}
return field;

It works like that, but the native select shows me the Id and the name.

try add this line of code

((NativeSelect) field).setItemCaptionMode(ItemCaptionMode.PROPERTY);

I’ve added the CaptionMode part but it seems like it has no effect, the NativeSelect still shows all atributes of the container. I’ve got another question, is it possible to bind another JPAContainer entity to a nativeSelect? Cause I have two more classes which need a container, but I already defined one in the createField method.

Something like

if(field instanceof NativeSelect) {
JPAContainer<Country> country = JPAContaiinerFactory.make(Country.class, TranslationUI.PERSISTENCE_UNIT);
((NativeSelect) field).setContainerDataSource(country);
((NativeSelect) field).setItemCaptionPropertyId("name");
}else if() {
JPAContainer<Language> language= JPAContaiinerFactory.make(Language.class, TranslationUI.PERSISTENCE_UNIT);
((NativeSelect) field).setContainerDataSource(language);
......
}

return field;
  1. your Country class seems doesn’t follow Java Bean standard. Do you mind to rename the field m_Name to name? and other field as well. Then the caption mode should work.
  2. the condtion inside if, you can make it like if(“country”.equals(propertyId.equals)) instead of checking the filed type.

Hopefully this can help

Thanks for your help Haijian! really appreciate it!

Hi there,
(VAADIN 6.8.5)
Please can anyone help me out with same problem?
I want to show nested form (OneToMany relationship)

But i am not able to find any proper solution.(not able to implement it)
if the list is empty , it would show empty textboxes with Add and Remove button
Can anybody share the sample code to implement the same form?

VERY URGENT.! :frowning:

Thanks in advance.

18311.png