Adding item to OneToMany collection in @SessionScoped CDI bean
onehumanunit Feb 11, 2014 6:18 PMHi all,
I am trying to add an item into a collection of an entity that is held inside a @SessionScoped bean. Using an Arquillian test case, I now want to access this entity. My code is as follows:
Thing.java
@Entity
public class Thing implements Serializable
{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private Long id;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "thing")
private Set<Widget> widgets;
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public Set<Widget> getWidgets()
{
return widgets;
}
public void setWidgets(Set<Widget> widgets)
{
this.widgets = widgets;
}
}
Widget.java:
@Entity
public class Widget implements Serializable
{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private Long id;
@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "thingId")
private Thing thing;
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public Thing getThing()
{
return thing;
}
public void setThing(Thing thing)
{
this.thing = thing;
}
}
ThingService.java:
@SessionScoped
public class ThingService implements Serializable
{
private static final long serialVersionUID = 1L;
@Inject
private ThingDataAccess thingDA;
@Inject
private EntityManager em;
private Thing thing;
public Thing saveThing()
{
thing = new Thing();
return thingDA.save(thing);
}
public Widget saveWidget()
{
Widget widget = new Widget();
widget.setThing(thing);
em.refresh(widget);
return thingDA.save(widget);
}
public Thing getThing()
{
return thing;
}
}
ThingDataAccess.java:
@Stateless
public class ThingDataAccess
{
@Inject
private EntityManager em;
public Thing save(Thing thing)
{
if(thing.getId() != null)
em.merge(thing);
else
em.persist(thing);
return thing;
}
public Widget save(Widget widget)
{
if(widget.getId() != null)
em.merge(widget);
else
em.persist(widget);
return widget;
}
}
ThingServiceTest.java:
@RunWith(Arquillian.class)
public class ThingServiceTest
{
@Deployment
public static Archive<?> createTestArchive()
{
return ShrinkWrap
.create(WebArchive.class, "test.war")
.addClasses(Thing.class, Widget.class, ThingDataAccess.class,
ThingService.class, Resources.class)
.addAsResource("META-INF/test-persistence.xml",
"META-INF/persistence.xml")
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
.addAsWebInfResource("test-ds.xml", "test-ds.xml");
}
@Inject
private ThingService service;
@Resource
private UserTransaction userTransaction;
private static Thing thing;
@Before
public void init() throws Exception
{
userTransaction.begin();
thing = service.saveThing();
}
@Test
@InSequence(1)
public void testThing() throws Exception
{
assertNotNull(thing);
userTransaction.commit();
}
@Test
@InSequence(2)
public void testWidget() throws Exception
{
Widget widget = service.saveWidget();
assertNotNull(widget);
thing = service.getThing();
assertNotNull(thing.getWidgets());
userTransaction.commit();
}
}
My questions to any helpful Arquillian evangelist out there would be:
- Is everything set up correctly?
- Why do I get java.lang.IllegalArgumentException: Entity not managed for my em.refresh(widget) statement?
- When I replace em.refresh(widget) with em.merge(widget), I get no errors, but the test fails with thing.getWidgets() returning null. Why is the thing's widget collection not also updated with the latest db state?
- Eventually, what I would like to test in testWidget() is assertTrue(thing.getWidgets().size() == 1). Is there a better way to do all this? For instance, can I get away without using UserTransaction? After all, we are calling with Servlet 3.0 protocol, and that should be session-friendly, right?
Many thanks in advance for any tips.