Pergunta

Good evening everybody, this is my first post on Stack Overflow. I have been quite recently introduced to Java 6 EE and, in particular, to JPA as part of the JSF 2.1 framework and I am now facing a strange behavior that I would like you to help me understand. In our project (developed using NetBeans 7.2) we have several one-to-many relationship and we would like to navigate them the same way we navigate many-to-one ones. The fact is that, instead, we are able to make them work as we want only after having restarted the application server (Glassfish 3.1.2) and, in addition, this behavior lasts only till the next deployment; which means we need to restart Glassfish every time we apply a modification... Here are some code excerpts to help you understand our situation.

This represents our main entity (Person) that has, among the others, a one-to-many relationship with Email as well as with Phone and a many-to-one relationship with AccountType

@Entity
public class Person implements Serializable {
    //
    //private non-collection fields including id 
    //

    @OneToMany(mappedBy="person", fetch=FetchType.EAGER)
    private Collection<Email> personEmails;

    @OneToMany(mappedBy="person")
    private Collection<Phone> personPhones;

    @ManyToOne
    private AccountType accountType;

    //
    // getter and setter, hashCode, isEqual and toString
    //
}

And these are Email...

@Entity
public class Email implements Serializable {
    //
    //private non-collection fields including id 
    //

    private String address;

    @ManyToOne
    private Person person;

    //
    // getter and setter, hashCode, isEqual and toString
    //
}

... Phone ...

@Entity
public class Phone implements Serializable {
    //
    //private non-collection fields including id 
    //

    private String number;

    @ManyToOne
    private Person person;

    //
    // getter and setter, hashCode, isEqual and toString
    //
}

... and AccountType

@Entity
public class AccounType implements Serializable {
    //
    //private non-collection fields including id 
    //

    private String name;

    @OneToMany(mappedBy="accountType")
    private Collection<Person> persons;

    //
    // getter and setter, hashCode, isEqual and toString
    //
}

We have then set up a sample page to test how that three fields in Person are actually fetched.

This represents the xhtml page...

<h:form id="form">

    <h:panelGrid columns="2">

        <h:outputLabel value="forename" />
        <h:outputLabel value="#{meBean.currentUser.forename}" />

        <h:outputLabel value="emails" />
        <h:outputLabel value="#{meBean.currentUser.personEmails.size()}" />

        <h:outputLabel value="phones" />
        <h:outputLabel value="#{meBean.currentUser.personPhones}" />

        <h:outputLabel value="accountType" />
        <h:outputLabel value="#{meBean.currentUser.accountType.name}" />

    </h:panelGrid>

</h:form>

... and this the controller

@ManagedBean
@RequestScoped
public class MeBean {

    @EJB
    private PersonFacade personFacade;
    private Person currentUser;

    public MeBean() {
        init();
    }

    @PostConstruct
    private void init() {
        // Hard-coding user details        
        try {
            this.currentUser = this.personFacade.getFromUsername("user1");
            this.currentUser.getPersonPhones().isEmpty();
        } catch (Exception e) {
        }
    }

    public Person getCurrentUser() {
        return currentUser;
    }

    public void setCurrentUser(Person currentUser) {
        this.currentUser = currentUser;
    }
}

Now, the result we get is the one we expect only if we access the page right after having restarted the application server.

forename    Alice
emails      2
phones      {[sums.groupa.entities.Phone[id=38]]}
accountType Student

If we modify anything (except for the view) and save, after the inevitable deploy, the result is different.

forename    Alice
emails      0
phones      {[]}
accountType Student

Why is that happening and how can we avoid it?

Thanks in advance.

AG

A couple of contributors (that I want to thank for their quick replies) asked for the PersonFacade implementation.

public Person getFromUsername(String username)
    {
        try
        {
            Query q = em.createQuery("SELECT p FROM Person p LEFT JOIN FETCH p.personEmails WHERE UPPER(p.username) = :username");
            q.setParameter("username", username.toUpperCase());
            return (Person) q.getSingleResult();
        }
        catch (Exception ex)
        {
            Logger.getLogger(PersonFacade.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        }
    }

As you can see I tried to use FETCH JOIN as suggested but the query is getting out too many results: it should fetch only one instance of Person representing Alice and containing two instances of Email in the personEmails field but I suspect it is getting two different instances of Person, each having a different instance of Email attached.

The original query was as follows:

SELECT p FROM Person p WHERE UPPER(p.username) = :username

Thanks again.

AG

Foi útil?

Solução

I don't know how you wrote your personFacade and got the Person entity.

But I guess you used query.

If you are using JPQL, try fetch join.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top