I have recently started playing around with the Play! Framework for Java, version 1.2.3 (the latest). While testing out the framework, I came across a strange problem when trying to persist a Map
object inside a Hibernate entity called FooSystem
. The Map object maps a long to a Hibernate entity I have called Foo
, with the declaration Map<Long, Foo> fooMap;
My problem is as follows: The correct tables are created as I have annotated them. However, when the FooSystem
object fs
is persisted, the data in fs.fooMap
is not!
Here is the code I am using for the entities. First is Foo
:
package models.test;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import play.db.jpa.Model;
@Entity
public class Foo extends Model
{
@ManyToOne
private FooSystem foosystem;
public Foo(FooSystem foosystem)
{
this.foosystem = foosystem;
}
}
And here is FooSystem
:
package models.test;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import play.db.jpa.Model;
@Entity
public class FooSystem extends Model
{
@ManyToMany(cascade = {CascadeType.ALL, CascadeType.PERSIST})
@JoinTable(
name = "fooMap",
joinColumns = @JoinColumn(name = "foosystem"),
inverseJoinColumns = @JoinColumn(name = "foo")
)
private Map<Long, Foo> fooMap = new HashMap<Long, Foo>();
public FooSystem()
{
Foo f1 = new Foo(this);
Foo f2 = new Foo(this);
fooMap.put(f1.getId(), f1);
fooMap.put(f2.getId(), f2);
}
public Map<Long, Foo> getFooMap()
{
return fooMap;
}
}
Here is the Controller
class I am using to test my set-up:
package controllers;
import javax.persistence.EntityManager;
import models.test.FooSystem;
import play.db.jpa.JPA;
import play.mvc.Controller;
public class TestController extends Controller
{
public static void index() {
EntityManager em = JPA.em();
FooSystem fs = new FooSystem();
em.persist(fs);
render();
}
}
The Play! framework automatically created a transaction for the HTTP request. Although data is inserted into the foo
and foosystem
tables, nothing is ever inserted into the foomap
table, which is the desired result. What can I do about this? What am I missing?
I managed to solve this problem using the advice of Java Ka Baby. The issue was actually not in my
Model
classes; the problem lay within theController
. Specifically, I was saving the entities in the wrong order. Once I realized that using the@ElementCollection
annotation on theMap<Long, Foo>
produced the same effects as the join table I was manually specifying, I tried I thought experiment where I re-thought how I was saving my entities.In the code I posted above, you can see in the
FooSystem
constructor that twoFoo
objects,f1
andf2
, are put intofooMap
before theFoo
objects are persisted. I realized that iff1
is not in the database when it is put into the map, how is JPA able to use its ID as a foreign key in the join table?If you can see where I'm going with this line of reasoning, you can see that the obvious answer is that JPA is not able to accomplish this amazing feat of using a foreign key to reference a nonexistent key. The bizarre thing is that the Play! console did not note any errors at all for the original code I posted, even though it was not correct at all. Either the framework swallowed every
Exception
thrown during the process, or I've written code that should produce anException
.So to fix the problem, I persisted the
Foo
entities before any operations were performed on them. Only then did I put them intofooMap
. Finally, oncefooMap
was populated, I persisted theFooSystem
entity.Here is the corrected
TestController
class:And, since I changed
FooSystem
, here is the final code for that class: