Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[eclipselink-users] (Long) Oddities using EclipseLink 2, NetBeans 6.8, EJB and GlassFish 3.0

Hi

This is my first foray into the field of EJB and JPA persistence using 
EclipseLink 2.0. Given my knowledge of these areas I've no idea where the 
problem lies and I hope that someone can point me in the right direction.

I've created enities, CLient and Phone (an abstract class) with  a bi-
directional @ManyToOne between them, and MobilePhone as a concrete entity 
class. I've used the NetBeans code generation tool to create the ClientFacade 
and PhoneFacade stateless session beans that actually perform the CRUD 
operation. I've then created a ClientService  stateless session bean that will 
contain the business logic that controls the CRUD operations. Lastly, I have 
my ClientServiceTest class that contains my unit tests.


package com.vasilikon.processes;

import com.vasilikon.entity.Client;
import com.vasilikon.entity.Example;
import com.vasilikon.entity.MobilePhone;
import com.vasilikon.exceptions.PhoneNumberException;
import javax.ejb.SessionContext;
import javax.ejb.embeddable.EJBContainer;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;

/**
 *
 * @author varleyr
 */
public class ClientServiceTest {

    static EJBContainer ejbc;
   

    public ClientServiceTest() {
    }



    @BeforeClass
    public static void setUpClass() throws Exception {
    ejbc = EJBContainer.createEJBContainer();

    }

    @AfterClass
    public static void tearDownClass() throws Exception {
    ejbc.close();
    }

    @Before
    public void setUp() {
    }

    @After
    public void tearDown() {
    }

    /**
     * Test of create method, of class ExampleService.
     */
    @Test
    public void testCreate() throws Exception {
        System.out.println("create");

        Client client = new Client();
        client.setFirstName("Roger");

        MobilePhone mobile = new MobilePhone(99887766);
        mobile.setClient(client);

        client.getPhones().add(mobile);

        ClientService instance = (ClientService) 
ejbc.getContext().lookup("java:global/classes/ClientService");
        instance.create(client);
        assertTrue(client.getId() != null);
        instance.delete(client);
    }

    @Test
    public void testCreateDuplicatePhoneNumber() throws Exception {
        System.out.println("create");

        Client client = new Client();
        client.setFirstName("Roger");

        MobilePhone mobile = new MobilePhone(99887766);
        mobile.setClient(client);

        client.getPhones().add(mobile);

        ClientService instance = (ClientService) 
ejbc.getContext().lookup("java:global/classes/ClientService");
        instance.create(client);
        assertTrue(client.getId() != null);

        Client dupclient = new Client();
        client.setFirstName("Fred");

        MobilePhone dupmobile = new MobilePhone(99887766);
        mobile.setClient(dupclient);

        dupclient.getPhones().add(dupmobile);

        try {
            instance.create(dupclient);
            fail();
        }
        catch (PhoneNumberException e) {
        }
        finally {
            instance.delete(client);
        }

    }

}


The first test in the class simply tests for entity creation, and works as 
expected and the client and phone entities are created with an client id of 1 
and are removed at the end of the test as expected.

However, the second test testCreateDuplicatePhoneNumber() behaves oddly. 
Stepping through under debug I see the first entity created and appearing in 
the database as expected with an assigned id of "2".  I also see the entry on 
the phone table associated with customer id 2.  I see the second entity class 
populated correctly with the duplicate phone number and firstName of Fred. The 
code correctly detects the duplicated phone number, does not persist the 
Client and then deletes the original client from the database. Now when I 
physically check the database, the Client table contains a row with an id  = 3 
and FirstName = null and the Phone table contains a fully populated record 
associated with Client_ID = 3.

And I have no idea why this is happening. I get the same result whether I 
specify the context.setRollbackOnly() or not. I'm guessing that because I see 
the row in the client table with a firstName value of null when I know that 
the entity instance was initialized to the value "Fred", somehow the Phone 
entity is being persisted and auto generating the back reference to the Client

Any pointers would be welcome. Source code is below

Regards

package com.vasilikon.entity;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

/**
 *
 * @author varleyr
 */
@Entity
public class Client implements Serializable {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String firstName;

    @OneToMany(mappedBy = "client", cascade=CascadeType.ALL)
    private List<Phone> phones;

    public Client() {
        this.phones = new ArrayList();
    }


    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public List<Phone> getPhones() {
        return phones;
    }

    public void setPhones(List<Phone> phones) {
        this.phones = phones;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields 
are not set
        if (!(object instanceof Client)) {
            return false;
        }
        Client other = (Client) object;
        if ((this.id == null && other.id != null) || (this.id != null && 
!this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "com.vasilikon.entity.Client[id=" + id + "]";
    }

}

package com.vasilikon.entity;

import java.util.List;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

/**
 *
 * @author varleyr
 */
@Stateless
@LocalBean
public class ClientFacade {
    @PersistenceContext(unitName = "Devere")
    private EntityManager em;

    public void create(Client client) {
        em.persist(client);
    }

    public void edit(Client client) {
        em.merge(client);
    }

    public void remove(Client client) {
        em.remove(em.merge(client));
    }

    public Client find(Object id) {
        return em.find(Client.class, id);
    }

    public List<Client> findAll() {
        CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
        cq.select(cq.from(Client.class));
        return em.createQuery(cq).getResultList();
    }

    public List<Client> findRange(int[] range) {
        CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
        cq.select(cq.from(Client.class));
        Query q = em.createQuery(cq);
        q.setMaxResults(range[1] - range[0]);
        q.setFirstResult(range[0]);
        return q.getResultList();
    }

    public int count() {
        CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
        Root<Client> rt = cq.from(Client.class);
        cq.select(em.getCriteriaBuilder().count(rt));
        Query q = em.createQuery(cq);
        return ((Long) q.getSingleResult()).intValue();
    }

}

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.vasilikon.entity;

import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

/**
 *
 * @author varleyr
 */
@Entity
public abstract class Phone implements Serializable {

    @Id
    private int number;

    @ManyToOne
    private Client client;
    private static final long serialVersionUID = 1L;



    public Phone() {

    }

    public Phone(int number) {
       this.number = number;
    }

   public int getId() {
       return number;
   }

   public void setId(int id) {
       this.number = id;
   }

    public Client getClient() {
        return client;
    }

    public void setClient(Client client) {
        this.client = client;
    }


    @Override
    public String toString() {
        return "com.vasilikon.entity.Phone[number=" + number + "]";
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Phone other = (Phone) obj;
        if (this.number != other.number) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 29 * hash + this.number;
        return hash;
    }

    

    

}

package com.vasilikon.entity;

import javax.persistence.Entity;

/**
 *
 * @author varleyr
 */
@Entity
public class MobilePhone extends Phone {

    
   
    public MobilePhone() {
        super();
    }

    public MobilePhone(int number) {
        super(number);
    }

}

package com.vasilikon.entity;

import java.util.List;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

/**
 *
 * @author varleyr
 */
@Stateless
@LocalBean
public class PhoneFacade {
    @PersistenceContext(unitName = "Devere")
    private EntityManager em;

    public void create(Phone phone) {
        em.persist(phone);
    }

    public void edit(Phone phone) {
        em.merge(phone);
    }

    public void remove(Phone phone) {
        em.remove(em.merge(phone));
    }

    public Phone find(Object id) {
        return em.find(Phone.class, id);
    }

    public List<Phone> findAll() {
        CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
        cq.select(cq.from(Phone.class));
        return em.createQuery(cq).getResultList();
    }

    public List<Phone> findRange(int[] range) {
        CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
        cq.select(cq.from(Phone.class));
        Query q = em.createQuery(cq);
        q.setMaxResults(range[1] - range[0]);
        q.setFirstResult(range[0]);
        return q.getResultList();
    }

    public int count() {
        CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
        Root<Phone> rt = cq.from(Phone.class);
        cq.select(em.getCriteriaBuilder().count(rt));
        Query q = em.createQuery(cq);
        return ((Long) q.getSingleResult()).intValue();
    }

}

package com.vasilikon.processes;

import com.vasilikon.entity.Client;
import com.vasilikon.entity.ClientFacade;
import com.vasilikon.entity.Phone;
import com.vasilikon.entity.PhoneFacade;
import com.vasilikon.exceptions.PhoneNumberException;
import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ejb.LocalBean;
import javax.ejb.SessionContext;

/**
 *
 * @author varleyr
 */
@Stateless
@LocalBean
public class ClientService {
    
    @EJB
    ClientFacade clientFacade;

    @EJB
    PhoneFacade phoneFacade;

    private SessionContext context;

    @Resource
    public void setSessionContext(SessionContext context) {
        this.context = context;
    }

    public void create(Client client) throws PhoneNumberException {

        for (Phone phone : client.getPhones()) {
            Phone exists = phoneFacade.find(phone.getId());
           
            if (exists != null) {
                 context.setRollbackOnly();
                throw new PhoneNumberException("Phone number " + phone.getId()
                        + " is already in use");
            }
        }

        clientFacade.create(client);
    }

    public void delete(Client client) {
        clientFacade.remove(client);
    }
}



Back to the top