[
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);
}
}