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.